Compare commits

..

37 Commits

Author SHA1 Message Date
Ria Bhatia
6d8b865848 Merge pull request #96 from arapulido/improve_azure_docs
Improve ACI provider documentation
2018-02-21 14:07:30 -08:00
Ara Pulido
3c87000b71 Improve ACI provider documentation based on my experience trying to set it up 2018-02-15 13:32:36 +01:00
Bhargav Nookala
a99f2d08c1 Adding supported regions for Azure Provider (#92) 2018-02-14 11:14:20 -08:00
Robbie Zhang
f4ebbfc7a3 [Azure] Optimize VK Setup in ACS/AKS (#85)
* Read ACS Credentials for Azure Authentication

Supprt a new environment variable: ACS_CREDENTIAL_LOCATION
Expect the value to be the ACS credential filepath, which is the
/etc/kubernetes/azure.json file generated on the ACS nodes.

If the ACS_CREDENTIAL_LOCATION is specified and loaded,
create the Azure Authentication class from its values.

If the AZURE_AUTHENTICATION_LOCATION is specified and loaded,
its values will overwrite the value above.

Refactor the ACI provider and ACI client to be able to override the SPN by environment variable
2018-02-13 19:07:27 -08:00
chshou
d23ac6679c append args (#89)
LGTM.
2018-02-08 14:20:35 -08:00
Nick Schuch
cf30854bb1 Provider: Mock (#72)
* Provider: Mock

* Add pod conditions

* Mock: Convert pod list to a map and allow 'internalIP' to be configurable
2018-02-01 09:48:42 -08:00
Robbie Zhang
6b6d71b5b9 Create the Issue Template 2018-01-31 21:07:11 -08:00
Patrick Lang
077263af25 Fix scheduling for Windows example (#83)
There shouldn't be a nodename selector. That prevents it from working in recent builds configured with `az aks install-connector`
2018-01-31 12:07:37 -08:00
Ria Bhatia
6486f65166 Merge pull request #75 from robbiezhang/imagepullsecrets
Fix the ImagePullSecret for ACI Provider
2018-01-29 10:07:39 -08:00
Ria Bhatia
ac90d3ab13 Merge pull request #79 from virtual-kubelet/rbitia-patch-4
Update README.md
2018-01-29 09:59:12 -08:00
Ria Bhatia
dadd97de8d Update README.md
changed release notes and added helm init to documentation
2018-01-29 09:58:58 -08:00
Ria Bhatia
35acb384e1 Merge pull request #77 from bnookala/node-exclusion-label-41
Node exclusion to labels, adding README section
2018-01-29 09:41:44 -08:00
Bhargav Nookala
5f9c46a80c Updating README 2018-01-26 00:45:57 -08:00
Bhargav Nookala
a48f9866f2 Adding node exclusion label 2018-01-26 00:16:32 -08:00
Ria Bhatia
c686fdffcb Update README.md 2018-01-25 21:27:31 -08:00
Ria Bhatia
4554635194 Merge pull request #76 from virtual-kubelet/rbitia-patch-3
Update README.md
2018-01-25 20:28:25 -08:00
Ria Bhatia
45630ed369 Update README.md
adding connector release notes
2018-01-25 20:28:14 -08:00
robbiezhang
4a8d43f736 Fix the ImagePullSecret for ACI Provider 2018-01-26 02:19:25 +00:00
Robbie Zhang
3668b0d6b9 Update the virtual-kubelet helm chart package (#70)
* Update the virtual-kubelet helm chart to use 0.2-beta-3 image

* Update the default secret values

* Update the helm package
2018-01-22 15:58:14 -08:00
Ria Bhatia
7d47eb1409 Update README.md 2018-01-22 15:26:23 -08:00
Ria Bhatia
f78a8e9522 Merge pull request #67 from virtual-kubelet/rbitia-patch-2
Update README.md
2018-01-22 13:44:36 -08:00
Ria Bhatia
980cea6a07 Update README.md
add wg slack channel
2018-01-22 13:44:12 -08:00
chshou
8c0345edcf [Azure] Filters service account secret volume mount for Windows (#60)
* filters the SA secret volume for windows

* make it a map

* bettern go convention
2018-01-22 11:19:53 -08:00
Ria Bhatia
9eb0cf535f Merge pull request #65 from robbiezhang/master
Fix the EmptyDir Volume Error in ACI Provider
2018-01-22 10:58:32 -08:00
Ria Bhatia
61d5440dc3 Merge pull request #66 from virtual-kubelet/rbitia-patch-1
Create README.md
2018-01-22 10:57:34 -08:00
Ria Bhatia
f9d1805ba0 Create README.md
add steps to be accepted as a provider
2018-01-22 10:57:23 -08:00
robbiezhang
909819c69d [Azure] Fix the EmptyDir Volume issue for ACI Provider 2018-01-20 02:32:38 +00:00
robbiezhang
d27616776c Fix the dep on cenkalti 2018-01-20 02:31:58 +00:00
Ria Bhatia
2ca933fe27 Update README.md
added a warning
2018-01-18 18:27:00 -08:00
Ria Bhatia
b92c7320ca Update README.md
adding a way to update your connector
2018-01-18 18:26:08 -08:00
Ria Bhatia
4fb11ecd87 Merge pull request #62 from virtual-kubelet/azureReadMe
updating azure readme
2018-01-18 14:43:16 -08:00
Ria Bhatia
a21cfff420 updating azure readme 2018-01-18 14:07:21 -08:00
Chen Shou
d690131565 add quotes 2018-01-18 11:26:06 -08:00
Rita Zhang
2c47b6b573 Update README 2018-01-11 12:37:35 -08:00
Sam J Sharpe
922364ee5f Correct typo kuberentes->kubernetes (#58)
This seems to be a common typo, possibly exacerbated by copy-paste
of comment strings. I spotted it in the README.md at first, so I fixed
that and then checked the rest of the code and fixed a few more. They
are all in comments or documentation, so there's no code issue.

FYI, there's still some in the vendor directory, but I haven't fixed
those as they'd get replaced by any new `go get` I think:

$ ack uberentes
vendor/k8s.io/apimachinery/hack/godep-deps.sh
20:# 2. godep restore based on k8s.io/kuberentes provided manifest

vendor/k8s.io/client-go/CHANGELOG.md
120:* Included bug fixes in k8s.io/kuberentes release-1.5 branch, up to

vendor/k8s.io/client-go/kubernetes/fake/register.go
64://     clientsetscheme "k8s.io/client-go/kuberentes/scheme"

vendor/k8s.io/client-go/kubernetes/scheme/register.go
64://     clientsetscheme "k8s.io/client-go/kuberentes/scheme"

Sorry, this isn't the most exciting commit in the world :-(
2018-01-11 12:37:04 -08:00
Ria Bhatia
87011f266e Merge pull request #55 from avranju/web
Implement web broker provider and a sample provider in Rust
2018-01-08 10:48:39 -08:00
Rajasekharan Vengalil
a4e99c2133 Implement web broker provider and a sample provider in Rust 2018-01-04 16:48:58 -08:00
62 changed files with 3767 additions and 241 deletions

3
.gitignore vendored
View File

@@ -22,3 +22,6 @@ bin/
# Test credentials file
credentials.json
# VS Code files
.vscode/

8
Gopkg.lock generated
View File

@@ -43,6 +43,12 @@
revision = "f006c2ac4710855cf0f916dd6b77acf6b048dc6e"
version = "v1.0.3"
[[projects]]
name = "github.com/cenkalti/backoff"
packages = ["."]
revision = "61153c768f31ee5f130071d08fc82b85208528de"
version = "v1.1.0"
[[projects]]
branch = "master"
name = "github.com/cloudfoundry-incubator/candiedyaml"
@@ -432,6 +438,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "dfaa92ef7d6d4569afb59e36336ce6e33207abb5b926be8d694bf653e05a22b8"
inputs-digest = "febdb92c3131da3213797c6f20b297c751e9bddf5938064989494a21c3017333"
solver-name = "gps-cdcl"
solver-version = 1

16
ISSUE_TEMPLATE.md Normal file
View File

@@ -0,0 +1,16 @@
---
### Environment summary
Provider (e.g. Azure, Hyper)
Version (e.g. 0.1, 0.2-beta)
K8s Master Info (e.g. AKS, ACS, Bare Metal)
Install Method (e.g. Helm Chart, )
### Issue Details
### Repo Steps

View File

@@ -3,7 +3,7 @@
Virtual Kubelet is an open source [Kubernetes kubelet](https://kubernetes.io/docs/reference/generated/kubelet/) implementation that masquerades as a kubelet for the purposes of connecting Kubernetes to other APIs. This allows the nodes to be backed by other services like ACI, Hyper.sh, AWS, etc. This connector features a pluggable architecture and direct use of Kubernetes primitives, making it much easier to build on.
We invite the Kubernetes ecosystem to join us in empowering developers to build
upon our base.
upon our base. Join our slack channel named, virtual-kubelet, within the [Kubernetes slack group](https://kubernetes.slack.com/).
Please note this software is experimental and should not be used for anything
resembling a production workload.
@@ -20,6 +20,7 @@ The best description is "Kubernetes API on top, programmable back."
+ [Adding a New Provider via the Provider Interface](#adding-a-new-provider-via-the-provider-interface)
* [Testing](#testing)
+ [Testing the Azure Provider Client](#testing-the-azure-provider-client)
* [Known quirks and workarounds](#known-quirks-and-workarounds)
* [Contributing](#contributing)
## How It Works
@@ -56,11 +57,11 @@ a `virtual-kubelet` node.
* Resource limits (Mem and Cores)
* Environment variables
* Public IPs
* kubectl logs
## Current Limitations
* kubectl exec
* kubectl logs
* Metrics
## Command-Line Usage
@@ -81,7 +82,7 @@ Available Commands:
Flags:
-h, --help help for virtual-kubelet
--kubeconfig string config file (default is $HOME/.kube/config)
--namespace string kuberentes namespace (default is 'all')
--namespace string kubernetes namespace (default is 'all')
--nodename string kubernetes node name (default "virtual-kubelet")
--os string Operating System (Linux/Windows) (default "Linux")
--provider string cloud provider
@@ -91,22 +92,6 @@ Flags:
Use "virtual-kubelet [command] --help" for more information about a command.
```
## Deploy as a Pod by Helm Chart
Run these commands to deploy the virtual kubelet which connects your Kubernetes cluster to Azure Container Instances.
If you want to run the connector from the Azure command-line check out this.
```bash
RELEASE_NAME=virtual-kubelet
CHART_URL=https://github.com/virtual-kubelet/virtual-kubelet/raw/master/charts/virtual-kubelet-0.1.0.tgz
curl https://raw.githubusercontent.com/virtual-kubelet/virtual-kubelet/master/scripts/createCertAndKey.sh > createCertAndKey.sh
. createCertAndKey.sh
helm install "$CHART_URL" --name "$RELEASE_NAME" \
--set env.azureClientId=<YOUR-AZURECLIENTID-HERE>,env.azureClientKey=<YOUR-AZURECLIENTKEY-HERE>,env.azureTenantId=<YOUR-AZURETENANTID-HERE>,env.azureSubscriptionId=<YOUR-AZURESUBSCRIPTIONID-HERE>,env.aciResourceGroup=<YOUR-ACIRESOURCEGROUP-HERE>,env.nodeName=<YOUR-NODE-NAME>,env.nodeOsType=<Linux|Windows>,env.nodeTaint=<YOUR-NODE-TAINT>,env.apiserverCert=$cert,env.apiserverKey=$key
```
## Providers
This project features a pluggable provider interface developers can implement
@@ -124,16 +109,7 @@ The Azure Container Instances Provider allows you to utilize both
typical pods on VMs and Azure Container instances simultaneously in the
same Kubernetes cluster.
```bash
./bin/virtual-kubelet --provider azure
```
#### Environment Variables
`ACI_RESOURCE_GROUP` must be set to the name of a valid Azure resource group where your
ACI workload will be run.
`ACI_REGION` must be set to the name of the region your `ACI_RESOURCE_GROUP` was created.
You can find detailed instructions on how to set it up and how to test it in the [Azure Container Instances Provider documentation](./providers/azure/README.md).
#### Configuration File
@@ -184,7 +160,7 @@ type Provider interface {
Capacity() v1.ResourceList
// NodeConditions returns a list of conditions (Ready, OutOfDisk, etc), which is polled periodically to update the node status
// within Kuberentes.
// within Kubernetes.
NodeConditions() []v1.NodeCondition
// OperatingSystem returns the operating system the provider is for.
@@ -205,6 +181,19 @@ set to a credentials file.
You can generate this file by following the instructions listed in the
[README](providers/azure/client/README.md) for that package.
## Known quirks and workarounds
### Missing Load Balancer IP addresses for services
#### When Virtual Kubelet is installed on a cluster, I cannot create external-IPs for a Service
Kubernetes 1.9 introduces a new flag, `ServiceNodeExclusion`, for the control plane's Controller Manager. Enabling this flag in the Controller Manager's manifest allows Kubernetes to exclude Virtual Kubelet nodes from being added to Load Balancer pools, allowing you to create public facing services with external IPs without issue.
#### Workaround
Cluster requirements: Kubernetes 1.9 or above
Enable the ServiceNodeExclusion flag, by modifying the Controller Manager manifest and adding `--feature-gates=ServiceNodeExclusion=true` to the command line arguments.
## Contributing
@@ -218,4 +207,4 @@ provided by the bot. You will only need to do this once across all repos using o
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,8 @@
name: virtual-kubelet-for-aks
version: 0.1.3
description: a Helm chart to install virtual kubelet in an AKS or ACS cluster.
sources:
- https://github.com/virtual-kubelet/virtual-kubelet
maintainers:
- name: Robbie Zhang
email: junjiez@microsoft.com

View File

@@ -0,0 +1,5 @@
The virtual kubelet is getting deployed on your cluster.
To verify that virtual kubelet has started, run:
kubectl --namespace={{ .Release.Namespace }} get pods -l "app={{ template "fullname" . }}"

View File

@@ -0,0 +1,16 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 24 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}

View File

@@ -0,0 +1,57 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: {{ template "fullname" . }}
spec:
replicas: 1
template:
metadata:
labels:
app: {{ template "fullname" . }}
spec:
containers:
- name: {{ template "fullname" . }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: KUBELET_PORT
value: "10250"
- name: ACS_CREDENTIAL_LOCATION
value: /etc/virtual-kubelet/acs.json
- name: AZURE_TENANT_ID
value: {{ .Values.env.azureTenantId }}
- name: AZURE_SUBSCRIPTION_ID
value: {{ .Values.env.azureSubscriptionId }}
- name: AZURE_CLIENT_ID
value: {{ .Values.env.azureClientId }}
- name: AZURE_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: {{ template "fullname" . }}
key: clientSecret
- name: ACI_RESOURCE_GROUP
value: {{ .Values.env.aciResourceGroup }}
- name: ACI_REGION
value: {{ default "westus" .Values.env.aciRegion }}
- name: APISERVER_CERT_LOCATION
value: /etc/virtual-kubelet/cert.pem
- name: APISERVER_KEY_LOCATION
value: /etc/virtual-kubelet/key.pem
- name: VKUBELET_POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
volumeMounts:
- name: credentials
mountPath: "/etc/virtual-kubelet"
- name: acs-credential
mountPath: "/etc/virtual-kubelet/acs.json"
command: ["virtual-kubelet"]
args: ["--provider", "azure", "--namespace", "default", "--nodename", {{ default "virtual-kubelet" .Values.env.nodeName | quote }} , "--os", {{ default "Linux" .Values.env.nodeOsType | quote }}, "--taint", {{ default "azure.com/aci" .Values.env.nodeTaint | quote }}]
volumes:
- name: credentials
secret:
secretName: {{ template "fullname" . }}
- name: acs-credential
hostPath:
path: /etc/kubernetes/azure.json

View File

@@ -0,0 +1,9 @@
apiVersion: v1
kind: Secret
metadata:
name: {{ template "fullname" . }}
type: Opaque
data:
cert.pem: {{ (default "TUlTU0lORw==" .Values.env.apiserverCert) | quote }}
key.pem: {{ (default "TUlTU0lORw==" .Values.env.apiserverKey) | quote }}
clientSecret: {{ default "" .Values.env.azureClientKey | b64enc | quote }}

View File

@@ -0,0 +1,16 @@
image:
repository: microsoft/virtual-kubelet
tag: 0.2-beta-6
pullPolicy: Always
env:
azureClientId:
azureClientKey:
azureTenantId:
azureSubscriptionId:
aciResourceGroup:
aciRegion:
nodeName:
nodeTaint:
nodeOsType:
apiserverCert:
apiserverKey:

View File

@@ -5,5 +5,5 @@ metadata:
type: Opaque
data:
credentials.json: {{ printf "{ \"clientId\": \"%s\", \"clientSecret\": \"%s\", \"subscriptionId\": \"%s\", \"tenantId\": \"%s\", \"activeDirectoryEndpointUrl\": \"https://login.microsoftonline.com/\", \"resourceManagerEndpointUrl\": \"https://management.azure.com/\", \"activeDirectoryGraphResourceId\": \"https://graph.windows.net/\", \"sqlManagementEndpointUrl\": \"database.windows.net\", \"galleryEndpointUrl\": \"https://gallery.azure.com/\", \"managementEndpointUrl\": \"https://management.core.windows.net/\" }" (default "MISSING" .Values.env.azureClientId) (default "MISSING" .Values.env.azureClientKey) (default "MISSING" .Values.env.azureSubscriptionId) (default "MISSING" .Values.env.azureTenantId) | b64enc | quote }}
cert.pem: {{ (default "MISSING" .Values.env.apiserverCert) | quote }}
key.pem: {{ (default "MISSING" .Values.env.apiserverKey) | quote }}
cert.pem: {{ (default "TUlTU0lORw==" .Values.env.apiserverCert) | quote }}
key.pem: {{ (default "TUlTU0lORw==" .Values.env.apiserverKey) | quote }}

View File

@@ -18,7 +18,6 @@ spec:
- containerPort: 443
name: https
dnsPolicy: Default
nodeName: virtual-kubelet-aciconnector-win
automountServiceAccountToken: false
tolerations:
- key: azure.com/aci

10
providers/README.md Normal file
View File

@@ -0,0 +1,10 @@
Follow these steps to be accepted as a provider within the Virtual Kubelet repo.
1. Replicate the life-cycle of a pod for example creation and deletion of a pod and how that maps to your service.
2. Create a new provider folder with a descriptive name and the necessary code.
3. When committing your code add a README.md, helm chart, dockerfile and specify a maintainer of the provider.
4. Within the PR itself add a justification for why the provider should be accepted, as well as customer use cases if applicable.
Some providers are translations of Virtual Kubelet to allow others to adapt their service or applications that are written in other languages.

View File

@@ -1,62 +1,212 @@
# Kubernetes ACI connector with AKS
# Kubernetes virtual-kubelet with ACI
Azure Container Instances (ACI) provide a hosted environment for running containers in Azure. When using ACI, there is no need to manage the underlying compute infrastructure, Azure handles this management for you. When running containers in ACI, you are charged by the second for each running container.
The Azure Container Instances connector for Kubernetes configures an ACI instance as a node in any Kubernetes cluster. When using the ACI connector for Kubernetes, pods can be scheduled on an ACI instance as if the ACI instance is a standard Kubernetes node. This configuration allows you to take advantage of both the capabilities of Kubernetes and the management value and cost benefit of ACI.
The Azure Container Instances provider for the Virtual Kubelet configures an ACI instance as a node in any Kubernetes cluster. When using the Virtual Kubelet ACI provider, pods can be scheduled on an ACI instance as if the ACI instance is a standard Kubernetes node. This configuration allows you to take advantage of both the capabilities of Kubernetes and the management value and cost benefit of ACI.
This document details configuring the ACI connector for Kubernetes on an Azure Container Service (AKS) cluster.
This document details configuring the Virtual Kubelet ACI provider.
## Prerequisite
The steps detailed in this document assume that you have created an AKS Kubernetes cluster and have established a kubectl connection with the cluster. If you need these items see, the [Azure Container Service (AKS) quickstart][aks-quick-start].
This guide assumes that you have a Kubernetes cluster up and running (can be `minikube`) and that `kubectl` is already configured to talk to it.
You also need the Azure CLI version **2.0.22** or later. Run `az --version` to find the version. If you need to install or upgrade, see [Install Azure CLI](/cli/azure/install-azure-cli).
Other pre-requesites are:
## Installation
* A [Microsoft Azure account](https://azure.microsoft.com/en-us/free/).
* Install the [Azure CLI](#install-the-azure-cli).
* Install the [Kubernetes CLI](#install-the-kubernetes-cli).
* Install the [Helm CLI](#install-the-helm-cli).
To install the ACI connector for an AKS cluster, run the following Azure CLI command. Replace the values for the following arguments:
### Install the Azure CLI
- resource-group - the resource group of the AKS cluster.
- name - the name of the AKS cluster.
- connector-name - the name given to the ACI connector.
Install `az` by following the instructions for your operating system.
See the [full installation instructions](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest) if yours isn't listed below.
```azurecli-interactive
az aks install-connector --resource-group myResourceGroup --name myAKSCluster --connector-name myaciconnector
#### MacOS
```console
brew install azure-cli
```
#### Windows
Download and run the [Azure CLI Installer (MSI)](https://aka.ms/InstallAzureCliWindows).
#### Ubuntu 64-bit
1. Add the azure-cli repo to your sources:
```console
echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ wheezy main" | \
sudo tee /etc/apt/sources.list.d/azure-cli.list
```
1. Run the following commands to install the Azure CLI and its dependencies:
```console
sudo apt-key adv --keyserver packages.microsoft.com --recv-keys 52E16F86FEE04B979B07E28DB02C46DF417A0893
sudo apt-get install apt-transport-https
sudo apt-get update && sudo apt-get install azure-cli
```
### Install the Kubernetes CLI
Install `kubectl` by running the following command:
```console
az aks install-cli
```
### Install the Helm CLI
[Helm](https://github.com/kubernetes/helm) is a tool for installing pre-configured applications on Kubernetes.
Install `helm` by running the following command:
#### MacOS
```console
brew install kubernetes-helm
```
#### Windows
1. Download the latest [Helm release](https://storage.googleapis.com/kubernetes-helm/helm-v2.7.2-windows-amd64.tar.gz).
1. Decompress the tar file.
1. Copy **helm.exe** to a directory on your PATH.
#### Linux
```console
curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get | bash
```
---
## Cluster and Azure Account Setup
Now that we have all the tools, we will set up your Azure account to work with ACI.
### Configure your Azure account
First let's identify your Azure subscription and save it for use later on in the quickstart.
1. Run `az login` and follow the instructions in the command output to authorize `az` to use your account
1. List your Azure subscriptions:
```console
az account list -o table
```
1. Copy your subscription ID and save it in an environment variable:
**Bash**
```console
export AZURE_SUBSCRIPTION_ID="<SubscriptionId>"
```
**PowerShell**
```console
$env:AZURE_SUBSCRIPTION_ID = "<SubscriptionId>"
```
### Create a Resource Group for ACI
To use Azure Container Instances, you must provide a resource group. Create one with the az cli using the following command.
```console
export ACI_REGION=eastus
az group create --name aci-group --location "$ACI_REGION"
export AZURE_RG=aci-group
```
### Create a service principal
This creates an identity for the Virtual Kubelet ACI provider to use when provisioning
resources on your account on behalf of Kubernetes.
1. Create a service principal with RBAC enabled for the quickstart:
```console
az ad sp create-for-rbac --name virtual-kubelet-quickstart -o table
```
1. Save the values from the command output in environment variables:
**Bash**
```console
export AZURE_TENANT_ID=<Tenant>
export AZURE_CLIENT_ID=<AppId>
export AZURE_CLIENT_SECRET=<Password>
```
**PowerShell**
```console
$env:AZURE_TENANT_ID = "<Tenant>"
$env:AZURE_CLIENT_ID = "<AppId>"
$env:AZURE_CLIENT_SECRET = "<Password>"
```
### Setting up your Azure account to use ACI
You will need to enable ACI in your subscription:
```console
az provider register -n Microsoft.ContainerInstance
```
## Deployment of the ACI provider in your cluster
Run these commands to deploy the virtual kubelet which connects your Kubernetes cluster to Azure Container Instances.
If your cluster is an AKS cluster:
```console
export VK_RELEASE=virtual-kubelet-for-aks-0.1.3
````
For any other type of Kubernetes cluster:
```console
export VK_RELEASE=virtual-kubelet-0.1.0
```
```console
RELEASE_NAME=virtual-kubelet
NODE_NAME=virtual-kubelet
CHART_URL=https://github.com/virtual-kubelet/virtual-kubelet/raw/master/charts/$VK_RELEASE.tgz
curl https://raw.githubusercontent.com/virtual-kubelet/virtual-kubelet/master/scripts/createCertAndKey.sh > createCertAndKey.sh
. createCertAndKey.sh
helm install "$CHART_URL" --name "$RELEASE_NAME" \
--set env.azureClientId="$AZURE_CLIENT_ID",env.azureClientKey="$AZURE_CLIENT_SECRET",env.azureTenantId="$AZURE_TENANT_ID",env.azureSubscriptionId="$AZURE_SUBSCRIPTION_ID",env.aciResourceGroup="$AZURE_RG",env.nodeName="$NODE_NAME",env.nodeOsType=<Linux|Windows>,env.apiserverCert=$cert,env.apiserverKey=$key
```
Output:
```
NAME: myaciconnector
LAST DEPLOYED: Tue Dec 5 21:12:33 2017
```console
NAME: virtual-kubelet
LAST DEPLOYED: Thu Feb 15 13:17:01 2018
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==> v1/Secret
NAME TYPE DATA AGE
myaciconnector-aci-connector Opaque 4 1s
NAME TYPE DATA AGE
virtual-kubelet-virtual-kubelet Opaque 3 1s
==> v1beta1/Deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
myaciconnector-aci-connector 1 1 1 0 1s
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
virtual-kubelet-virtual-kubelet 1 1 1 0 1s
==> v1/Pod(related)
NAME READY STATUS RESTARTS AGE
myaciconnector-aci-connector-1218204046-cn81d 0/1 ContainerCreating 0 1s
NAME READY STATUS RESTARTS AGE
virtual-kubelet-virtual-kubelet-7bcf5dc749-6mvgp 0/1 ContainerCreating 0 1s
NOTES:
The aci-connector is getting deployed on your cluster.
The virtual kubelet is getting deployed on your cluster.
To verify that aci-connector has started, run:
To verify that virtual kubelet has started, run:
kubectl --namespace=default get pods -l "app=myaciconnector-aci-connector"
kubectl --namespace=default get pods -l "app=virtual-kubelet-virtual-kubelet"
```
## Validate the ACI connector
## Validate the Virtual Kubelet ACI provider
To validate that the ACI connector has been installed, return a list of Kubernetes nodes using the [kubectl get nodes][kubectl-get] command. You should see a node that matches the name given to the ACI connector.
To validate that the Virtual Kubelet has been installed, return a list of Kubernetes nodes using the [kubectl get nodes][kubectl-get] command. You should see a node that matches the name given to the ACI connector.
```azurecli-interactive
kubectl get nodes
@@ -65,59 +215,64 @@ kubectl get nodes
Output:
```console
NAME STATUS ROLES AGE VERSION
aci-connector Ready <none> 2m v1.6.6
aks-nodepool1-39289454-0 Ready agent 22h v1.7.7
aks-nodepool1-39289454-1 Ready agent 22h v1.7.7
aks-nodepool1-39289454-2 Ready agent 22h v1.7.7
NAME STATUS ROLES AGE VERSION
virtual-kubelet Ready <none> 2m v1.8.3
aks-nodepool1-39289454-0 Ready agent 22h v1.7.7
aks-nodepool1-39289454-1 Ready agent 22h v1.7.7
aks-nodepool1-39289454-2 Ready agent 22h v1.7.7
```
## Schedule a pod in ACI
Create a file named `aci-connector-test.yaml` and copy in the following YAML. Replace the `nodeName` value with the name given to the ACI connector.
Create a file named `virtual-kubelet-test.yaml` and copy in the following YAML. Replace the `nodeName` value with the name given to the virtual kubelet node.
```yaml
apiVersion: apps/v1beta1
kind: Deployment
apiVersion: v1
kind: Pod
metadata:
name: aci-helloworld
name: helloworld
spec:
replicas: 1
template:
metadata:
labels:
app: aci-helloworld
spec:
containers:
- name: aci-helloworld
image: microsoft/aci-helloworld
ports:
- containerPort: 80
nodeName: aci-connector
containers:
- image: microsoft/aci-helloworld
imagePullPolicy: Always
name: helloworld
resources:
requests:
memory: 1G
cpu: 1
ports:
- containerPort: 80
name: http
protocol: TCP
- containerPort: 443
name: https
dnsPolicy: ClusterFirst
nodeName: virtual-kubelet
```
Run the application with the [kubectl create][kubectl-create] command.
```azurecli-interactive
kubectl create -f aci-connector-test.yml
```console
kubectl create -f virtual-kubelet-test.yml
```
Use the [kubectl get pods][kubectl-get] command with the `-o wide` argument to output a list of pods with the scheduled node.
```azurecli-interactive
```console
kubectl get pods -o wide
```
Notice that the `kube-aci-demo` pod is running on the `myACIConnector` node.
Notice that the `helloworld` pod is running on the `virtual-kubelet` node.
```console
NAME READY STATUS RESTARTS AGE IP NODE
aci-helloworld-2559879000-8vmjw 1/1 Running 0 39s 52.179.3.180 aci-connector
aci-helloworld-2559879000-8vmjw 1/1 Running 0 39s 52.179.3.180 virtual-kubelet
```
To validate that the container is running in an Azure Container Instance, use the [az container list][az-container-list] Azure CLI command.
```azurecli-interactive
```cli
az container list -o table
```
@@ -126,19 +281,18 @@ Output:
```console
Name ResourceGroup ProvisioningState Image IP:ports CPU/Memory OsType Location
------------------------------- --------------- ------------------- ------------------------ --------------- --------------- -------- ----------
aci-helloworld-2559879000-8vmjw myAKSCluster2 Succeeded microsoft/aci-helloworld 52.179.3.180:80 1.0 core/1.5 gb Linux eastus
helloworld-2559879000-8vmjw myResourceGroup Succeeded microsoft/aci-helloworld 52.179.3.180:80 1.0 core/1.5 gb Linux eastus
```
## Remove the ACI connector
## Remove the Virtual Kubelet
To remove the ACI connector, run the following command. Replace the argument values with the name of the connector, AKS cluster, and the AKS cluster resource group.
You can remove your Virtual Kubelet node, you can delete the Helm deployment, by running the following command:
```azurecli-interactive
az aks remove-connector --resource-group myResourceGroup --name myAKSCluster --connector-name myaciconnector
```
helm delete virtual-kubelet --purge
```
<!-- LINKS -->
[aks-quick-start]: https://docs.microsoft.com/en-us/azure/aks/kubernetes-walkthrough
[kubectl-create]: https://kubernetes.io/docs/user-guide/kubectl/v1.6/#create
[kubectl-get]: https://kubernetes.io/docs/user-guide/kubectl/v1.8/#get
[az-container-list]: https://docs.microsoft.com/en-us/cli/azure/aks?view=azure-cli-latest#az_aks_list
[az-container-list]: https://docs.microsoft.com/en-us/cli/azure/aks?view=azure-cli-latest#az_aks_list

View File

@@ -9,9 +9,12 @@ import (
"log"
"net/http"
"os"
"reflect"
"strings"
"time"
"github.com/virtual-kubelet/virtual-kubelet/manager"
client "github.com/virtual-kubelet/virtual-kubelet/providers/azure/client"
"github.com/virtual-kubelet/virtual-kubelet/providers/azure/client/aci"
"k8s.io/api/core/v1"
k8serr "k8s.io/apimachinery/pkg/api/errors"
@@ -20,19 +23,22 @@ import (
"k8s.io/apimachinery/pkg/types"
)
// The service account secret mount path.
const serviceAccountSecretMountPath = "/var/run/secrets/kubernetes.io/serviceaccount"
// ACIProvider implements the virtual-kubelet provider interface and communicates with Azure's ACI APIs.
type ACIProvider struct {
aciClient *aci.Client
resourceManager *manager.ResourceManager
resourceGroup string
region string
nodeName string
operatingSystem string
cpu string
memory string
pods string
internalIP string
daemonEndpointPort int32
aciClient *aci.Client
resourceManager *manager.ResourceManager
resourceGroup string
region string
nodeName string
operatingSystem string
cpu string
memory string
pods string
internalIP string
daemonEndpointPort int32
}
// AuthConfig is the secret returned from an ImageRegistryCredential
@@ -46,6 +52,28 @@ type AuthConfig struct {
RegistryToken string `json:"registrytoken,omitempty"`
}
// See https://docs.microsoft.com/en-us/azure/container-instances/container-instances-quotas for valid regions.
var validAciRegions = []string{
"westeurope",
"westus",
"eastus",
"southeastasia",
}
// isValidACIRegion checks to make sure we're using a valid ACI region
func isValidACIRegion(region string) bool {
regionLower := strings.ToLower(region)
regionTrimmed := strings.Replace(regionLower, " ", "", -1)
for _, validRegion := range validAciRegions {
if regionTrimmed == validRegion {
return true
}
}
return false
}
// NewACIProvider creates a new ACIProvider.
func NewACIProvider(config string, rm *manager.ResourceManager, nodeName, operatingSystem string, internalIP string, daemonEndpointPort int32) (*ACIProvider, error) {
var p ACIProvider
@@ -53,11 +81,6 @@ func NewACIProvider(config string, rm *manager.ResourceManager, nodeName, operat
p.resourceManager = rm
p.aciClient, err = aci.NewClient()
if err != nil {
return nil, err
}
if config != "" {
f, err := os.Open(config)
if err != nil {
@@ -70,6 +93,61 @@ func NewACIProvider(config string, rm *manager.ResourceManager, nodeName, operat
}
}
var azAuth *client.Authentication
if authFilepath := os.Getenv("AZURE_AUTH_LOCATION"); authFilepath != "" {
auth, err := client.NewAuthenticationFromFile(authFilepath)
if err != nil {
return nil, err
}
azAuth = auth
}
if acsFilepath := os.Getenv("ACS_CREDENTIAL_LOCATION"); acsFilepath != "" {
acsCredential, err := NewAcsCredential(acsFilepath)
if err != nil {
return nil, err
}
if acsCredential != nil {
if acsCredential.Cloud != client.PublicCloud.Name {
return nil, fmt.Errorf("ACI only supports Public Azure. '%v' is not supported", acsCredential.Cloud)
}
azAuth = client.NewAuthentication(
acsCredential.Cloud,
acsCredential.ClientID,
acsCredential.ClientSecret,
acsCredential.SubscriptionID,
acsCredential.TenantID)
p.resourceGroup = acsCredential.ResourceGroup
p.region = acsCredential.Region
}
}
if clientID := os.Getenv("AZURE_CLIENT_ID"); clientID != "" {
azAuth.ClientID = clientID
}
if clientSecret := os.Getenv("AZURE_CLIENT_SECRET"); clientSecret != "" {
azAuth.ClientSecret = clientSecret
}
if tenantID := os.Getenv("AZURE_TENANT_ID"); tenantID != "" {
azAuth.TenantID = tenantID
}
if subscriptionID := os.Getenv("AZURE_SUBSCRIPTION_ID"); subscriptionID != "" {
azAuth.SubscriptionID = subscriptionID
}
p.aciClient, err = aci.NewClient(azAuth)
if err != nil {
return nil, err
}
if rg := os.Getenv("ACI_RESOURCE_GROUP"); rg != "" {
p.resourceGroup = rg
}
@@ -83,6 +161,12 @@ func NewACIProvider(config string, rm *manager.ResourceManager, nodeName, operat
if p.region == "" {
return nil, errors.New("Region can not be empty please set ACI_REGION")
}
if r := p.region; !isValidACIRegion(r) {
unsupportedRegionMessage := fmt.Sprintf("Region %s is invalid. Current supported regions are: %s",
r, strings.Join(validAciRegions, ", "))
return nil, errors.New(unsupportedRegionMessage)
}
// Set sane defaults for Capacity in case config is not supplied
p.cpu = "20"
@@ -125,6 +209,8 @@ func (p *ACIProvider) CreatePod(pod *v1.Pod) error {
containerGroup.ContainerGroupProperties.Volumes = volumes
containerGroup.ContainerGroupProperties.ImageRegistryCredentials = creds
filterServiceAccountSecretVolume(p.operatingSystem, &containerGroup)
// create ipaddress if containerPort is used
count := 0
for _, container := range containers {
@@ -196,7 +282,7 @@ func (p *ACIProvider) GetPod(namespace, name string) (*v1.Pod, error) {
return containerGroupToPod(cg)
}
// GetPodLogs returns the logs of a pod by name that is running inside ACI.
// GetContainerLogs returns the logs of a pod by name that is running inside ACI.
func (p *ACIProvider) GetContainerLogs(namespace, podName, containerName string, tail int) (string, error) {
logContent := ""
cg, err, _ := p.aciClient.GetContainerGroup(p.resourceGroup, fmt.Sprintf("%s-%s", namespace, podName))
@@ -273,7 +359,7 @@ func (p *ACIProvider) Capacity() v1.ResourceList {
}
// NodeConditions returns a list of conditions (Ready, OutOfDisk, etc), for updates to the node status
// within Kuberentes.
// within Kubernetes.
func (p *ACIProvider) NodeConditions() []v1.NodeCondition {
// TODO: Make these dynamic and augment with custom ACI specific conditions of interest
return []v1.NodeCondition{
@@ -321,7 +407,7 @@ func (p *ACIProvider) NodeConditions() []v1.NodeCondition {
}
// NodeAddresses returns a list of addresses for the node status
// within Kuberentes.
// within Kubernetes.
func (p *ACIProvider) NodeAddresses() []v1.NodeAddress {
// TODO: Make these dynamic and augment with custom ACI specific conditions of interest
return []v1.NodeAddress{
@@ -333,7 +419,7 @@ func (p *ACIProvider) NodeAddresses() []v1.NodeAddress {
}
// NodeDaemonEndpoints returns NodeDaemonEndpoints for the node status
// within Kuberentes.
// within Kubernetes.
func (p *ACIProvider) NodeDaemonEndpoints() *v1.NodeDaemonEndpoints {
return &v1.NodeDaemonEndpoints{
KubeletEndpoint: v1.DaemonEndpoint{
@@ -360,26 +446,24 @@ func (p *ACIProvider) getImagePullSecrets(pod *v1.Pod) ([]aci.ImageRegistryCrede
// TODO: Check if secret type is v1.SecretTypeDockercfg and use DockerConfigKey instead of hardcoded value
// TODO: Check if secret type is v1.SecretTypeDockerConfigJson and use DockerConfigJsonKey to determine if it's in json format
// TODO: Return error if it's not one of these two types
repoDataB64, ok := secret.Data[".dockercfg"]
repoData, ok := secret.Data[".dockercfg"]
if !ok {
return ips, fmt.Errorf("no dockercfg present in secret")
}
repoData, err := base64.StdEncoding.DecodeString(string(repoDataB64))
var authConfigs map[string]AuthConfig
err = json.Unmarshal(repoData, &authConfigs)
if err != nil {
return ips, err
}
var ac AuthConfig
err = json.Unmarshal(repoData, &ac)
if err != nil {
return ips, err
for server, authConfig := range authConfigs {
ips = append(ips, aci.ImageRegistryCredential{
Password: authConfig.Password,
Server: server,
Username: authConfig.Username,
})
}
ips = append(ips, aci.ImageRegistryCredential{
Password: ac.Password,
Server: ac.ServerAddress,
Username: ac.Username,
})
}
return ips, nil
}
@@ -391,7 +475,7 @@ func (p *ACIProvider) getContainers(pod *v1.Pod) ([]aci.Container, error) {
Name: container.Name,
ContainerProperties: aci.ContainerProperties{
Image: container.Image,
Command: container.Command,
Command: append(container.Command, container.Args...),
Ports: make([]aci.ContainerPort, 0, len(container.Ports)),
},
}
@@ -452,7 +536,7 @@ func (p *ACIProvider) getVolumes(pod *v1.Pod) ([]aci.Volume, error) {
}
if secret == nil {
return nil, fmt.Errorf("Getting secret for AzureFile volume returned an empty secret.")
return nil, fmt.Errorf("Getting secret for AzureFile volume returned an empty secret")
}
volumes = append(volumes, aci.Volume{
@@ -470,11 +554,8 @@ func (p *ACIProvider) getVolumes(pod *v1.Pod) ([]aci.Volume, error) {
// Handle the case for the EmptyDir.
if v.EmptyDir != nil {
volumes = append(volumes, aci.Volume{
Name: v.Name,
EmptyDir: map[string]interface{}{
"medium": v.EmptyDir.Medium,
"sizeLimit": v.EmptyDir.SizeLimit,
},
Name: v.Name,
EmptyDir: map[string]interface{}{},
})
continue
}
@@ -713,3 +794,39 @@ func aciContainerStateToContainerState(cs aci.ContainerState) v1.ContainerState
},
}
}
// Filters service account secret volume for Windows.
// Service account secret volume gets automatically turned on if not specified otherwise.
// ACI doesn't support secret volume for Windows, so we need to filter it.
func filterServiceAccountSecretVolume(osType string, containerGroup *aci.ContainerGroup) {
if strings.EqualFold(osType, "Windows") {
serviceAccountSecretVolumeName := make(map[string]bool)
for index, container := range containerGroup.ContainerGroupProperties.Containers {
volumeMounts := make([]aci.VolumeMount, 0, len(container.VolumeMounts))
for _, volumeMount := range container.VolumeMounts {
if !strings.EqualFold(serviceAccountSecretMountPath, volumeMount.MountPath) {
volumeMounts = append(volumeMounts, volumeMount)
} else {
serviceAccountSecretVolumeName[volumeMount.Name] = true
}
}
containerGroup.ContainerGroupProperties.Containers[index].VolumeMounts = volumeMounts
}
if len(serviceAccountSecretVolumeName) == 0 {
return
}
log.Printf("Ignoring service account secret volumes '%v' for Windows", reflect.ValueOf(serviceAccountSecretVolumeName).MapKeys())
volumes := make([]aci.Volume, 0, len(containerGroup.ContainerGroupProperties.Volumes))
for _, volume := range containerGroup.ContainerGroupProperties.Volumes {
if _, ok := serviceAccountSecretVolumeName[volume.Name]; !ok {
volumes = append(volumes, volume)
}
}
containerGroup.ContainerGroupProperties.Volumes = volumes
}
}

View File

@@ -0,0 +1,38 @@
package azure
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
)
// AcsCredential represents the credential file for ACS
type AcsCredential struct {
Cloud string `json:"cloud"`
TenantID string `json:"tenantId"`
SubscriptionID string `json:"subscriptionId"`
ClientID string `json:"aadClientId"`
ClientSecret string `json:"aadClientSecret"`
ResourceGroup string `json:"resourceGroup"`
Region string `json:"location"`
}
// NewAcsCredential returns an AcsCredential struct from file path
func NewAcsCredential(filepath string) (*AcsCredential, error) {
log.Printf("Reading ACS credential file %q", filepath)
b, err := ioutil.ReadFile(filepath)
if err != nil {
return nil, fmt.Errorf("Reading ACS credential file %q failed: %v", filepath, err)
}
// Unmarshal the authentication file.
var cred AcsCredential
if err := json.Unmarshal(b, &cred); err != nil {
return nil, err
}
log.Printf("Load ACS credential file %q successfully", filepath)
return &cred, nil
}

View File

@@ -0,0 +1,129 @@
package azure
import (
"io/ioutil"
"os"
"testing"
)
const cred = `
{
"cloud":"AzurePublicCloud",
"tenantId": "72f988bf-86f1-41af-91ab-2d7cd011db47",
"subscriptionId": "11122233-4444-5555-6666-777888999000",
"aadClientId": "123",
"aadClientSecret": "456",
"resourceGroup": "vk-test-rg",
"location": "westcentralus",
"subnetName": "k8s-subnet",
"securityGroupName": "k8s-master-nsg",
"vnetName": "k8s-vnet",
"routeTableName": "k8s-master-routetable",
"primaryAvailabilitySetName": "agentpool1-availabilitySet",
"cloudProviderBackoff": true,
"cloudProviderBackoffRetries": 6,
"cloudProviderBackoffExponent": 1.5,
"cloudProviderBackoffDuration": 5,
"cloudProviderBackoffJitter": 1,
"cloudProviderRatelimit": true,
"cloudProviderRateLimitQPS": 3,
"cloudProviderRateLimitBucket": 10
}`
func TestAcsCred(t *testing.T) {
file, err := ioutil.TempFile("", "acs_test")
if err != nil {
t.Error(err)
}
defer os.Remove(file.Name())
if _, err := file.Write([]byte(cred)); err != nil {
t.Error(err)
}
cred, err := NewAcsCredential(file.Name())
if err != nil {
t.Error(err)
}
wanted := "AzurePublicCloud"
if cred.Cloud != wanted {
t.Errorf("Wanted %s, got %s.", wanted, cred.Cloud)
}
wanted = "72f988bf-86f1-41af-91ab-2d7cd011db47"
if cred.TenantID != wanted {
t.Errorf("Wanted %s, got %s.", wanted, cred.TenantID)
}
wanted = "11122233-4444-5555-6666-777888999000"
if cred.SubscriptionID != wanted {
t.Errorf("Wanted %s, got %s.", wanted, cred.SubscriptionID)
}
wanted = "123"
if cred.ClientID != wanted {
t.Errorf("Wanted %s, got %s.", wanted, cred.ClientID)
}
wanted = "456"
if cred.ClientSecret != wanted {
t.Errorf("Wanted %s, got %s.", wanted, cred.ClientSecret)
}
wanted = "vk-test-rg"
if cred.ResourceGroup != wanted {
t.Errorf("Wanted %s, got %s.", wanted, cred.ResourceGroup)
}
wanted = "westcentralus"
if cred.Region != wanted {
t.Errorf("Wanted %s, got %s.", wanted, cred.Region)
}
}
func TestAcsCredFileNotFound(t *testing.T) {
file, err := ioutil.TempFile("", "acs_test")
if err != nil {
t.Error(err)
}
fileName := file.Name()
if err := file.Close(); err != nil {
t.Error(err)
}
os.Remove(fileName)
if _, err := NewAcsCredential(fileName); err == nil {
t.Fatal("expected to fail with bad json")
}
}
const credBad = `
{
"cloud":"AzurePublicCloud",
"tenantId": "72f988bf-86f1-41af-91ab-2d7cd011db47",
"subscriptionId": "11122233-4444-5555-6666-777888999000",
"aadClientId": "123",
"aadClientSecret": "456",
"resourceGroup": "vk-test-rg",
"location": "westcentralus",
"subnetName": "k8s-subnet",`
func TestAcsCredBadJson(t *testing.T) {
file, err := ioutil.TempFile("", "acs_test")
if err != nil {
t.Error(err)
}
defer os.Remove(file.Name())
if _, err := file.Write([]byte(credBad)); err != nil {
t.Error(err)
}
if _, err := NewAcsCredential(file.Name()); err == nil {
t.Fatal("expected to fail with bad json")
}
}

View File

@@ -29,16 +29,14 @@ type Client struct {
}
// NewClient creates a new Azure Container Instances client.
func NewClient() (*Client, error) {
// Get authentication credentials from file.
auth, err := azure.NewAuthenticationFromFile()
if err != nil {
return nil, fmt.Errorf("Creating azure authentication from file failed: %v", err)
func NewClient(auth *azure.Authentication) (*Client, error) {
if auth == nil {
return nil, fmt.Errorf("Authentication is not supplied for the Azure client")
}
client, err := azure.NewClient(auth, BaseURI, userAgent)
if err != nil {
return nil, fmt.Errorf("Creating azure client failed: %v", err)
return nil, fmt.Errorf("Creating Azure client failed: %v", err)
}
return &Client{hc: client.HTTPClient, auth: auth}, nil

View File

@@ -3,11 +3,10 @@ package aci
import (
"log"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
azure "github.com/virtual-kubelet/virtual-kubelet/providers/azure/client"
"github.com/virtual-kubelet/virtual-kubelet/providers/azure/client/resourcegroups"
"github.com/google/uuid"
)
@@ -20,23 +19,6 @@ var (
)
func init() {
// Check if the AZURE_AUTH_LOCATION variable is already set.
// If it is not set, set it to the root of this project in a credentials.json file.
if os.Getenv("AZURE_AUTH_LOCATION") == "" {
// Check if the credentials.json file exists in the root of this project.
_, filename, _, _ := runtime.Caller(0)
dir := filepath.Dir(filename)
file := filepath.Join(dir, "../../../../credentials.json")
// Check if the file exists.
if _, err := os.Stat(file); os.IsNotExist(err) {
log.Fatalf("Either set AZURE_AUTH_LOCATION or add a credentials.json file to the root of this project.")
}
// Set the environment variable for the authentication file.
os.Setenv("AZURE_AUTH_LOCATION", file)
}
// Create a resource group name with uuid.
uid := uuid.New()
resourceGroup += "-" + uid.String()[0:6]
@@ -45,8 +27,13 @@ func init() {
// The TestMain function creates a resource group for testing
// and deletes in when it's done.
func TestMain(m *testing.M) {
auth, err := azure.NewAuthenticationFromFile("../../../../credentials.json")
if err != nil {
log.Fatalf("Failed to load Azure authentication file: %v", err)
}
// Check if the resource group exists and create it if not.
rgCli, err := resourcegroups.NewClient()
rgCli, err := resourcegroups.NewClient(auth)
if err != nil {
log.Fatalf("creating new resourcegroups client failed: %v", err)
}
@@ -84,11 +71,17 @@ func TestMain(m *testing.M) {
}
func TestNewClient(t *testing.T) {
var err error
client, err = NewClient()
auth, err := azure.NewAuthenticationFromFile("../../../../credentials.json")
if err != nil {
log.Fatalf("Failed to load Azure authentication file: %v", err)
}
c, err := NewClient(auth)
if err != nil {
t.Fatal(err)
}
client = c
}
func TestCreateContainerGroupFails(t *testing.T) {

View File

@@ -263,7 +263,7 @@ type UsageListResult struct {
type Volume struct {
Name string `json:"name,omitempty"`
AzureFile *AzureFileVolume `json:"azureFile,omitempty"`
EmptyDir map[string]interface{} `json:"emptyDir,omitempty"`
EmptyDir map[string]interface{} `json:"emptyDir"`
Secret map[string]string `json:"secret,omitempty"`
GitRepo *GitRepoVolume `json:"gitRepo,omitempty"`
}

View File

@@ -6,19 +6,11 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"unicode/utf16"
"github.com/dimchansky/utfbom"
)
const (
// AuthenticationFilepathName defines the name of the environment variable
// containing the path to the file to be used to populate Authentication
// for Azure.
AuthenticationFilepathName = "AZURE_AUTH_LOCATION"
)
// Authentication represents the authentication file for Azure.
type Authentication struct {
ClientID string `json:"clientId,omitempty"`
@@ -35,32 +27,49 @@ type Authentication struct {
// NewAuthentication returns an authentication struct from user provided
// credentials.
func NewAuthentication(clientID, clientSecret, subscriptionID, tenantID string) *Authentication {
func NewAuthentication(azureCloud, clientID, clientSecret, subscriptionID, tenantID string) *Authentication {
environment := PublicCloud
switch azureCloud {
case PublicCloud.Name:
environment = PublicCloud
break;
case USGovernmentCloud.Name:
environment = USGovernmentCloud
break;
case ChinaCloud.Name:
environment = ChinaCloud
break;
case GermanCloud.Name:
environment = GermanCloud
break;
}
return &Authentication{
ClientID: clientID,
ClientSecret: clientSecret,
SubscriptionID: subscriptionID,
TenantID: tenantID,
ClientID: clientID,
ClientSecret: clientSecret,
SubscriptionID: subscriptionID,
TenantID: tenantID,
ActiveDirectoryEndpoint: environment.ActiveDirectoryEndpoint,
ResourceManagerEndpoint: environment.ResourceManagerEndpoint,
GraphResourceID: environment.GraphEndpoint,
SQLManagementEndpoint: environment.SQLDatabaseDNSSuffix,
GalleryEndpoint: environment.GalleryEndpoint,
ManagementEndpoint: environment.ServiceManagementEndpoint,
}
}
// NewAuthenticationFromFile returns an authentication struct from file located
// at AZURE_AUTH_LOCATION.
func NewAuthenticationFromFile() (*Authentication, error) {
file := os.Getenv(AuthenticationFilepathName)
if file == "" {
return nil, fmt.Errorf("Authentication file not found, environment variable %s is not set", AuthenticationFilepathName)
}
b, err := ioutil.ReadFile(file)
// NewAuthenticationFromFile returns an authentication struct from file path
func NewAuthenticationFromFile(filepath string) (*Authentication, error) {
b, err := ioutil.ReadFile(filepath)
if err != nil {
return nil, fmt.Errorf("Reading authentication file %q failed: %v", file, err)
return nil, fmt.Errorf("Reading authentication file %q failed: %v", filepath, err)
}
// Authentication file might be encoded.
decoded, err := decode(b)
if err != nil {
return nil, fmt.Errorf("Decoding authentication file %q failed: %v", file, err)
return nil, fmt.Errorf("Decoding authentication file %q failed: %v", filepath, err)
}
// Unmarshal the authentication file.

View File

@@ -26,16 +26,14 @@ type Client struct {
}
// NewClient creates a new Azure resource groups client.
func NewClient() (*Client, error) {
// Get authentication credentials from file.
auth, err := azure.NewAuthenticationFromFile()
if err != nil {
return nil, fmt.Errorf("Creating azure authentication from file failed: %v", err)
func NewClient(auth *azure.Authentication) (*Client, error) {
if auth == nil {
return nil, fmt.Errorf("Authentication is not supplied for the Azure client")
}
client, err := azure.NewClient(auth, BaseURI, userAgent)
if err != nil {
return nil, fmt.Errorf("Creating azure client failed: %v", err)
return nil, fmt.Errorf("Creating Azure client failed: %v", err)
}
return &Client{hc: client.HTTPClient, auth: auth}, nil

View File

@@ -1,13 +1,10 @@
package resourcegroups
import (
"log"
"os"
"path/filepath"
"runtime"
"testing"
"github.com/google/uuid"
azure "github.com/virtual-kubelet/virtual-kubelet/providers/azure/client"
)
var (
@@ -17,34 +14,23 @@ var (
)
func init() {
// Check if the AZURE_AUTH_LOCATION variable is already set.
// If it is not set, set it to the root of this project in a credentials.json file.
if os.Getenv("AZURE_AUTH_LOCATION") == "" {
// Check if the credentials.json file exists in the root of this project.
_, filename, _, _ := runtime.Caller(0)
dir := filepath.Dir(filename)
file := filepath.Join(dir, "../../../../credentials.json")
// Check if the file exists.
if _, err := os.Stat(file); os.IsNotExist(err) {
log.Fatalf("Either set AZURE_AUTH_LOCATION or add a credentials.json file to the root of this project.")
}
// Set the environment variable for the authentication file.
os.Setenv("AZURE_AUTH_LOCATION", file)
}
// Create a resource group name with uuid.
uid := uuid.New()
resourceGroup += "-" + uid.String()[0:6]
}
func TestNewClient(t *testing.T) {
var err error
client, err = NewClient()
auth, err := azure.NewAuthenticationFromFile("../../../../credentials.json")
if err != nil {
t.Fatalf("Failed to load Azure authentication file: %v", err)
}
c, err := NewClient(auth)
if err != nil {
t.Fatal(err)
}
client = c
}
func TestResourceGroupDoesNotExist(t *testing.T) {

View File

@@ -3,6 +3,6 @@
Region = "westus"
ResourceGroup = "virtual-kubeletrg"
OperatingSystem = "Linux"
CPU = 100
Memory = 100Gi
Pods = 50
CPU = "100"
Memory = "100Gi"
Pods = "50"

View File

@@ -368,7 +368,7 @@ func (p *HyperProvider) Capacity() v1.ResourceList {
}
// NodeConditions returns a list of conditions (Ready, OutOfDisk, etc), for updates to the node status
// within Kuberentes.
// within Kubernetes.
func (p *HyperProvider) NodeConditions() []v1.NodeCondition {
// TODO: Make these dynamic and augment with custom hyper.sh specific conditions of interest
return []v1.NodeCondition{
@@ -417,13 +417,13 @@ func (p *HyperProvider) NodeConditions() []v1.NodeCondition {
}
// NodeAddresses returns a list of addresses for the node status
// within Kuberentes.
// within Kubernetes.
func (p *HyperProvider) NodeAddresses() []v1.NodeAddress {
return nil
}
// NodeDaemonEndpoints returns NodeDaemonEndpoints for the node status
// within Kuberentes.
// within Kubernetes.
func (p *HyperProvider) NodeDaemonEndpoints() *v1.NodeDaemonEndpoints {
return nil
}

260
providers/mock/mock.go Normal file
View File

@@ -0,0 +1,260 @@
package mock
import (
"log"
"time"
"fmt"
"github.com/virtual-kubelet/virtual-kubelet/providers"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// MockProvider implements the virtual-kubelet provider interface and stores pods in memory.
type MockProvider struct {
nodeName string
operatingSystem string
internalIP string
daemonEndpointPort int32
pods map[string]*v1.Pod
}
// NewMockProvider creates a new MockProvider
func NewMockProvider(nodeName, operatingSystem string, internalIP string, daemonEndpointPort int32) (*MockProvider, error) {
provider := MockProvider{
nodeName: nodeName,
operatingSystem: operatingSystem,
internalIP: internalIP,
daemonEndpointPort: daemonEndpointPort,
pods: make(map[string]*v1.Pod),
}
return &provider, nil
}
// CreatePod accepts a Pod definition and stores it in memory.
func (p *MockProvider) CreatePod(pod *v1.Pod) error {
log.Printf("receive CreatePod %q\n", pod.Name)
key, err := buildKey(pod)
if err != nil {
return err
}
p.pods[key] = pod
return nil
}
// UpdatePod accepts a Pod definition and updates its reference.
func (p *MockProvider) UpdatePod(pod *v1.Pod) error {
log.Printf("receive UpdatePod %q\n", pod.Name)
key, err := buildKey(pod)
if err != nil {
return err
}
p.pods[key] = pod
return nil
}
// DeletePod deletes the specified pod out of memory.
func (p *MockProvider) DeletePod(pod *v1.Pod) (err error) {
log.Printf("receive DeletePod %q\n", pod.Name)
key, err := buildKey(pod)
if err != nil {
return err
}
delete(p.pods, key)
return nil
}
// GetPod returns a pod by name that is stored in memory.
func (p *MockProvider) GetPod(namespace, name string) (pod *v1.Pod, err error) {
log.Printf("receive GetPod %q\n", pod.Name)
key, err := buildKey(pod)
if err != nil {
return nil, err
}
if pod, ok := p.pods[key]; ok {
return pod, nil
}
return nil, nil
}
// GetContainerLogs retrieves the logs of a container by name from the provider.
func (p *MockProvider) GetContainerLogs(namespace, podName, containerName string, tail int) (string, error) {
log.Printf("receive GetContainerLogs %q\n", podName)
return "", nil
}
// GetPodStatus returns the status of a pod by name that is "running".
// returns nil if a pod by that name is not found.
func (p *MockProvider) GetPodStatus(namespace, name string) (*v1.PodStatus, error) {
log.Printf("receive GetPodStatus %q\n", name)
now := metav1.NewTime(time.Now())
status := &v1.PodStatus{
Phase: v1.PodRunning,
HostIP: "1.2.3.4",
PodIP: "5.6.7.8",
StartTime: &now,
Conditions: []v1.PodCondition{
{
Type: v1.PodInitialized,
Status: v1.ConditionTrue,
},
{
Type: v1.PodReady,
Status: v1.ConditionTrue,
},
{
Type: v1.PodScheduled,
Status: v1.ConditionTrue,
},
},
}
pod, err := p.GetPod(namespace, name)
if err != nil {
return status, err
}
for _, container := range pod.Spec.Containers {
status.ContainerStatuses = append(status.ContainerStatuses, v1.ContainerStatus{
Name: container.Name,
Image: container.Image,
Ready: true,
RestartCount: 0,
State: v1.ContainerState{
Running: &v1.ContainerStateRunning{
StartedAt: now,
},
},
})
}
return status, nil
}
// GetPods returns a list of all pods known to be "running".
func (p *MockProvider) GetPods() ([]*v1.Pod, error) {
log.Printf("receive GetPods\n")
var pods []*v1.Pod
for _, pod := range p.pods {
pods = append(pods, pod)
}
return pods, nil
}
// Capacity returns a resource list containing the capacity limits.
func (p *MockProvider) Capacity() v1.ResourceList {
// TODO: These should be configurable
return v1.ResourceList{
"cpu": resource.MustParse("20"),
"memory": resource.MustParse("100Gi"),
"pods": resource.MustParse("20"),
}
}
// NodeConditions returns a list of conditions (Ready, OutOfDisk, etc), for updates to the node status
// within Kubernetes.
func (p *MockProvider) NodeConditions() []v1.NodeCondition {
// TODO: Make this configurable
return []v1.NodeCondition{
{
Type: "Ready",
Status: v1.ConditionTrue,
LastHeartbeatTime: metav1.Now(),
LastTransitionTime: metav1.Now(),
Reason: "KubeletReady",
Message: "kubelet is ready.",
},
{
Type: "OutOfDisk",
Status: v1.ConditionFalse,
LastHeartbeatTime: metav1.Now(),
LastTransitionTime: metav1.Now(),
Reason: "KubeletHasSufficientDisk",
Message: "kubelet has sufficient disk space available",
},
{
Type: "MemoryPressure",
Status: v1.ConditionFalse,
LastHeartbeatTime: metav1.Now(),
LastTransitionTime: metav1.Now(),
Reason: "KubeletHasSufficientMemory",
Message: "kubelet has sufficient memory available",
},
{
Type: "DiskPressure",
Status: v1.ConditionFalse,
LastHeartbeatTime: metav1.Now(),
LastTransitionTime: metav1.Now(),
Reason: "KubeletHasNoDiskPressure",
Message: "kubelet has no disk pressure",
},
{
Type: "NetworkUnavailable",
Status: v1.ConditionFalse,
LastHeartbeatTime: metav1.Now(),
LastTransitionTime: metav1.Now(),
Reason: "RouteCreated",
Message: "RouteController created a route",
},
}
}
// NodeAddresses returns a list of addresses for the node status
// within Kubernetes.
func (p *MockProvider) NodeAddresses() []v1.NodeAddress {
return []v1.NodeAddress{
{
Type: "InternalIP",
Address: p.internalIP,
},
}
}
// NodeDaemonEndpoints returns NodeDaemonEndpoints for the node status
// within Kubernetes.
func (p *MockProvider) NodeDaemonEndpoints() *v1.NodeDaemonEndpoints {
return &v1.NodeDaemonEndpoints{
KubeletEndpoint: v1.DaemonEndpoint{
Port: p.daemonEndpointPort,
},
}
}
// OperatingSystem returns the operating system for this provider.
// This is a noop to default to Linux for now.
func (p *MockProvider) OperatingSystem() string {
return providers.OperatingSystemLinux
}
// buildKey is a helper for building the "key" for the providers pod store.
func buildKey(pod *v1.Pod) (string, error) {
if pod.ObjectMeta.Namespace == "" {
return "", fmt.Errorf("pod namespace not found")
}
if pod.ObjectMeta.Name == "" {
return "", fmt.Errorf("pod name not found")
}
return fmt.Sprintf("%s-%s", pod.ObjectMeta.Namespace, pod.ObjectMeta.Name), nil
}

277
providers/web/broker.go Normal file
View File

@@ -0,0 +1,277 @@
// Package web provides an implementation of the virtual kubelet provider interface
// by forwarding all calls to a web endpoint. The web endpoint to which requests
// must be forwarded must be specified through an environment variable called
// `WEB_ENDPOINT_URL`. This endpoint must implement the following HTTP APIs:
// - POST /createPod
// - PUT /updatePod
// - DELETE /deletePod
// - GET /getPod?namespace=[namespace]&name=[pod name]
// - GE /getContainerLogs?namespace=[namespace]&podName=[pod name]&containerName=[container name]&tail=[tail value]
// - GET /getPodStatus?namespace=[namespace]&name=[pod name]
// - GET /getPods
// - GET /capacity
// - GET /nodeConditions
// - GET /nodeAddresses
package web
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"time"
"github.com/cenkalti/backoff"
"k8s.io/api/core/v1"
)
// BrokerProvider implements the virtual-kubelet provider interface by forwarding kubelet calls to a web endpoint.
type BrokerProvider struct {
nodeName string
operatingSystem string
endpoint *url.URL
client *http.Client
daemonEndpointPort int32
}
// NewBrokerProvider creates a new BrokerProvider
func NewBrokerProvider(nodeName, operatingSystem string, daemonEndpointPort int32) (*BrokerProvider, error) {
var provider BrokerProvider
provider.nodeName = nodeName
provider.operatingSystem = operatingSystem
provider.client = &http.Client{}
provider.daemonEndpointPort = daemonEndpointPort
if ep := os.Getenv("WEB_ENDPOINT_URL"); ep != "" {
epurl, err := url.Parse(ep)
if err != nil {
return nil, err
}
provider.endpoint = epurl
}
return &provider, nil
}
// CreatePod accepts a Pod definition and forwards the call to the web endpoint
func (p *BrokerProvider) CreatePod(pod *v1.Pod) error {
return p.createUpdatePod(pod, "POST", "/createPod")
}
// UpdatePod accepts a Pod definition and forwards the call to the web endpoint
func (p *BrokerProvider) UpdatePod(pod *v1.Pod) error {
return p.createUpdatePod(pod, "PUT", "/updatePod")
}
// DeletePod accepts a Pod definition and forwards the call to the web endpoint
func (p *BrokerProvider) DeletePod(pod *v1.Pod) error {
urlPath, err := url.Parse("/deletePod")
if err != nil {
return err
}
// encode pod definition as JSON and post request
podJSON, err := json.Marshal(pod)
if err != nil {
return err
}
_, err = p.doRequest("DELETE", urlPath, podJSON, false)
return err
}
// GetPod returns a pod by name that is being managed by the web server
func (p *BrokerProvider) GetPod(namespace, name string) (*v1.Pod, error) {
urlPathStr := fmt.Sprintf(
"/getPod?namespace=%s&name=%s",
url.QueryEscape(namespace),
url.QueryEscape(name))
var pod v1.Pod
err := p.doGetRequest(urlPathStr, &pod)
// if we get a "404 Not Found" then we return nil to indicate that no pod
// with this name was found
if err != nil && err.Error() == "404 Not Found" {
return nil, nil
}
return &pod, err
}
// GetContainerLogs returns the logs of a container running in a pod by name.
func (p *BrokerProvider) GetContainerLogs(namespace, podName, containerName string, tail int) (string, error) {
urlPathStr := fmt.Sprintf(
"/getContainerLogs?namespace=%s&podName=%s&containerName=%s&tail=%d",
url.QueryEscape(namespace),
url.QueryEscape(podName),
url.QueryEscape(containerName),
tail)
response, err := p.doGetRequestBytes(urlPathStr)
if err != nil {
return "", err
}
return string(response), nil
}
// GetPodStatus retrieves the status of a given pod by name.
func (p *BrokerProvider) GetPodStatus(namespace, name string) (*v1.PodStatus, error) {
urlPathStr := fmt.Sprintf(
"/getPodStatus?namespace=%s&name=%s",
url.QueryEscape(namespace),
url.QueryEscape(name))
var podStatus v1.PodStatus
err := p.doGetRequest(urlPathStr, &podStatus)
// if we get a "404 Not Found" then we return nil to indicate that no pod
// with this name was found
if err != nil && err.Error() == "404 Not Found" {
return nil, nil
}
return &podStatus, err
}
// GetPods retrieves a list of all pods scheduled to run.
func (p *BrokerProvider) GetPods() ([]*v1.Pod, error) {
var pods []*v1.Pod
err := p.doGetRequest("/getPods", &pods)
return pods, err
}
// Capacity returns a resource list containing the capacity limits
func (p *BrokerProvider) Capacity() v1.ResourceList {
var resourceList v1.ResourceList
err := p.doGetRequest("/capacity", &resourceList)
// TODO: This API should support reporting an error.
if err != nil {
panic(err)
}
return resourceList
}
// NodeConditions returns a list of conditions (Ready, OutOfDisk, etc), for updates to the node status
func (p *BrokerProvider) NodeConditions() []v1.NodeCondition {
var nodeConditions []v1.NodeCondition
err := p.doGetRequest("/nodeConditions", &nodeConditions)
// TODO: This API should support reporting an error.
if err != nil {
panic(err)
}
return nodeConditions
}
// NodeAddresses returns a list of addresses for the node status
// within Kubernetes.
func (p *BrokerProvider) NodeAddresses() []v1.NodeAddress {
var nodeAddresses []v1.NodeAddress
err := p.doGetRequest("/nodeAddresses", &nodeAddresses)
// TODO: This API should support reporting an error.
if err != nil {
panic(err)
}
return nodeAddresses
}
// NodeDaemonEndpoints returns NodeDaemonEndpoints for the node status
// within Kubernetes.
func (p *BrokerProvider) NodeDaemonEndpoints() *v1.NodeDaemonEndpoints {
return &v1.NodeDaemonEndpoints{
KubeletEndpoint: v1.DaemonEndpoint{
Port: p.daemonEndpointPort,
},
}
}
// OperatingSystem returns the operating system for this provider.
func (p *BrokerProvider) OperatingSystem() string {
return p.operatingSystem
}
func (p *BrokerProvider) doGetRequest(urlPathStr string, v interface{}) error {
response, err := p.doGetRequestBytes(urlPathStr)
if err != nil {
return err
}
return json.Unmarshal(response, &v)
}
func (p *BrokerProvider) doGetRequestBytes(urlPathStr string) ([]byte, error) {
urlPath, err := url.Parse(urlPathStr)
if err != nil {
return nil, err
}
return p.doRequest("GET", urlPath, nil, true)
}
func (p *BrokerProvider) createUpdatePod(pod *v1.Pod, method, postPath string) error {
// build the post url
postPathURL, err := url.Parse(postPath)
if err != nil {
return err
}
// encode pod definition as JSON and post request
podJSON, err := json.Marshal(pod)
if err != nil {
return err
}
_, err = p.doRequest(method, postPathURL, podJSON, false)
return err
}
func (p *BrokerProvider) doRequest(method string, urlPath *url.URL, body []byte, readResponse bool) ([]byte, error) {
// build full URL
requestURL := p.endpoint.ResolveReference(urlPath)
// build the request
var bodyReader io.Reader
if body != nil {
bodyReader = bytes.NewReader(body)
}
request, err := http.NewRequest(method, requestURL.String(), bodyReader)
request.Header.Add("Content-Type", "application/json")
// issue request
retry := backoff.NewExponentialBackOff()
retry.MaxElapsedTime = 5 * time.Minute
var response *http.Response
err = backoff.Retry(func() error {
response, err = p.client.Do(request)
return err
}, retry)
if err != nil {
return nil, err
}
defer response.Body.Close()
if response.StatusCode < 200 || response.StatusCode > 299 {
return nil, errors.New(response.Status)
}
// read response body if asked to
if readResponse {
return ioutil.ReadAll(response.Body)
}
return nil, nil
}

View File

@@ -0,0 +1,21 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj

View File

@@ -0,0 +1,4 @@
name: virtual-kubelet-web
version: 0.1.0
description: a Helm chart to install virtual kubelet in Kubernetes setup with a web provider

View File

@@ -0,0 +1,5 @@
The virtual kubelet is getting deployed on your cluster.
To verify that virtual kubelet has started, run:
kubectl --namespace={{ .Release.Namespace }} get pods -l "app={{ template "virtual-kubelet-web.fullname" . }}"

View File

@@ -0,0 +1,16 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "virtual-kubelet-web.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "virtual-kubelet-web.fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}

View File

@@ -0,0 +1,42 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: {{ template "virtual-kubelet-web.fullname" . }}
labels:
app: {{ template "virtual-kubelet-web.name" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: 1
selector:
matchLabels:
app: {{ template "virtual-kubelet-web.name" . }}
template:
metadata:
labels:
app: {{ template "virtual-kubelet-web.name" . }}
release: {{ .Release.Name }}
spec:
containers:
- name: rustwebprovider
image: "{{ .Values.rustwebimage.repository }}:{{ .Values.rustwebimage.tag }}"
imagePullPolicy: {{ .Values.rustwebimage.pullPolicy }}
ports:
- containerPort: {{ .Values.rustwebimage.port }}
livenessProbe:
httpGet:
path: /
port: {{ .Values.rustwebimage.port }}
readinessProbe:
httpGet:
path: /
port: {{ .Values.rustwebimage.port }}
- name: virtualkubelet
image: "{{ .Values.vkimage.repository }}:{{ .Values.vkimage.tag }}"
imagePullPolicy: {{ .Values.vkimage.pullPolicy }}
env:
- name: WEB_ENDPOINT_URL
value: http://localhost:{{ .Values.rustwebimage.port }}
command: ["virtual-kubelet"]
args: ["--provider", "web", "--nodename", {{ default "web-provider" .Values.env.nodeName | quote }}]

View File

@@ -0,0 +1,11 @@
rustwebimage:
repository: avranju/rust-web-provider
tag: latest
pullPolicy: Always
port: 3000
vkimage:
repository: avranju/virtual-kubelet
tag: latest
pullPolicy: Always
env:
nodeName: virtual-kubelet-web

View File

@@ -0,0 +1,4 @@
/target/
**/*.rs.bk
.idea/
.vscode/

988
providers/web/rust-web-provider/Cargo.lock generated Normal file
View File

@@ -0,0 +1,988 @@
[[package]]
name = "aho-corasick"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "base64"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "base64"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "base64"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bitflags"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bitflags"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bodyparser"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"iron 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"persistent 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "byteorder"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bytes"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cfg-if"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "dtoa"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "env_logger"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon-sys"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "futures"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "futures-cpupool"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "httparse"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "hyper"
version = "0.10.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
"traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "hyper"
version = "0.11.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"base64 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-cpupool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"relay 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "idna"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "iovec"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "iron"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"mime_guess 1.8.3 (registry+https://github.com/rust-lang/crates.io-index)",
"modifier 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "itoa"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "kube_rust"
version = "1.0.0"
source = "git+https://github.com/avranju/kube-rust?rev=058de6366d0d75cb60b2d0fd5ba1abd2e7d83fff#058de6366d0d75cb60b2d0fd5ba1abd2e7d83fff"
dependencies = [
"base64 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.11.10 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_yaml 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "language-tags"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lazy_static"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lazycell"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "linked-hash-map"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "log"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "log"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "matches"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "memchr"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mime"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mime"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mime_guess"
version = "1.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
"phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mio"
version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazycell 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "miow"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "modifier"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "net2"
version = "0.2.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num"
version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-bigint 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
"num-complex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
"num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
"num-rational 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-bigint"
version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-complex"
version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-integer"
version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-iter"
version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-rational"
version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-bigint 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
"num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-traits"
version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "num_cpus"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "percent-encoding"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "persistent"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"iron 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "phf"
version = "0.7.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "phf_codegen"
version = "0.7.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
"phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "phf_generator"
version = "0.7.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "phf_shared"
version = "0.7.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "plugin"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rand"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fuchsia-zircon 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "redox_syscall"
version = "0.1.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "regex"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex-syntax"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "relay"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "route-recognizer"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "router"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"iron 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"route-recognizer 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rust-web-provider"
version = "0.1.0"
dependencies = [
"env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"kube_rust 1.0.0 (git+https://github.com/avranju/kube-rust?rev=058de6366d0d75cb60b2d0fd5ba1abd2e7d83fff)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
"virtual-kubelet-adapter 0.1.0 (git+https://github.com/avranju/rust-virtual-kubelet-adapter?rev=4250103d31e2864725e47bdd23295e79ee12b6d0)",
]
[[package]]
name = "rustc-serialize"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "safemem"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "scoped-tls"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde_derive"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_derive_internals"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
"synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_json"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_yaml"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"linked-hash-map 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"yaml-rust 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "siphasher"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "slab"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "slab"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "smallvec"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "synom"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "take"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "thread_local"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "time"
version = "0.1.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-core"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
"scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-io"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-proto"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-service"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "traitobject"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "typeable"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "typemap"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unsafe-any 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicase"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicase"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-bidi"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-normalization"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-xid"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unreachable"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unsafe-any"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "url"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "utf8-ranges"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "version_check"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "virtual-kubelet-adapter"
version = "0.1.0"
source = "git+https://github.com/avranju/rust-virtual-kubelet-adapter?rev=4250103d31e2864725e47bdd23295e79ee12b6d0#4250103d31e2864725e47bdd23295e79ee12b6d0"
dependencies = [
"bodyparser 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"iron 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"kube_rust 1.0.0 (git+https://github.com/avranju/kube-rust?rev=058de6366d0d75cb60b2d0fd5ba1abd2e7d83fff)",
"num 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
"persistent 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"router 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "ws2_32-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "yaml-rust"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"linked-hash-map 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata]
"checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4"
"checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9"
"checksum base64 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5032d51da2741729bfdaeb2664d9b8c6d9fd1e2b90715c660b6def36628499c2"
"checksum base64 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "229d032f1a99302697f10b27167ae6d03d49d032e6a8e2550e8d3fc13356d2b4"
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
"checksum bodyparser 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f023abfa58aad6f6bc4ae0630799e24d5ee0ab8bb2e49f651d9b1f9aa4f52f30"
"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23"
"checksum bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d828f97b58cc5de3e40c421d0cf2132d6b2da4ee0e11b8632fa838f0f9333ad6"
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
"checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b"
"checksum fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f6c0581a4e363262e52b87f59ee2afe3415361c6ec35e665924eb08afe8ff159"
"checksum fuchsia-zircon 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bd510087c325af53ba24f3be8f1c081b0982319adcb8b03cad764512923ccc19"
"checksum fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "43f3795b4bae048dc6123a6b972cadde2e676f9ded08aef6bb77f5f157684a82"
"checksum fuchsia-zircon-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "08b3a6f13ad6b96572b53ce7af74543132f1a7055ccceb6d073dd36c54481859"
"checksum futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "118b49cac82e04121117cbd3121ede3147e885627d82c4546b87c702debb90c1"
"checksum futures-cpupool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "e86f49cc0d92fe1b97a5980ec32d56208272cbb00f15044ea9e2799dde766fdf"
"checksum httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "af2f2dd97457e8fb1ae7c5a420db346af389926e36f43768b96f101546b04a07"
"checksum hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)" = "368cb56b2740ebf4230520e2b90ebb0461e69034d85d1945febd9b3971426db2"
"checksum hyper 0.11.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4844b207be8393981c5fcb61c9372d7c96432fcc8f5c3431a255a9d19b5c298b"
"checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d"
"checksum iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b6e8b9c2247fcf6c6a1151f1156932be5606c9fd6f55a2d7f9fc1cb29386b2f7"
"checksum iron 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d8e17268922834707e1c29e8badbf9c712c9c43378e1b6a3388946baff10be2"
"checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum kube_rust 1.0.0 (git+https://github.com/avranju/kube-rust?rev=058de6366d0d75cb60b2d0fd5ba1abd2e7d83fff)" = "<none>"
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
"checksum lazycell 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3b585b7a6811fb03aa10e74b278a0f00f8dd9b45dc681f148bb29fa5cb61859b"
"checksum libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)" = "36fbc8a8929c632868295d0178dd8f63fc423fd7537ad0738372bd010b3ac9b0"
"checksum linked-hash-map 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2aab0478615bb586559b0114d94dd8eca4fdbb73b443adcb0d00b61692b4bf"
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
"checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2"
"checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376"
"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
"checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0"
"checksum mime 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e2e00e17be181010a91dbfefb01660b17311059dc8c7f48b9017677721e732bd"
"checksum mime_guess 1.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "dc7e82a15629bb4ecd9e72365bf33d1382be91e030f820edb8e2a21c02430da8"
"checksum mio 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0e8411968194c7b139e9105bc4ae7db0bae232af087147e72f0616ebf5fdb9cb"
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
"checksum modifier 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41f5c9112cb662acd3b204077e0de5bc66305fa8df65c8019d5adb10e9ab6e58"
"checksum net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)" = "3a80f842784ef6c9a958b68b7516bc7e35883c614004dd94959a4dca1b716c09"
"checksum num 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "cc4083e14b542ea3eb9b5f33ff48bd373a92d78687e74f4cc0a30caeb754f0ca"
"checksum num-bigint 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "bdc1494b5912f088f260b775799468d9b9209ac60885d8186a547a0476289e23"
"checksum num-complex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "58de7b4bf7cf5dbecb635a5797d489864eadd03b107930cbccf9e0fd7428b47c"
"checksum num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "d1452e8b06e448a07f0e6ebb0bb1d92b8890eea63288c0b627331d53514d0fba"
"checksum num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "7485fcc84f85b4ecd0ea527b14189281cf27d60e583ae65ebc9c088b13dffe01"
"checksum num-rational 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "0c7cb72a95250d8a370105c828f388932373e0e94414919891a0f945222310fe"
"checksum num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "cacfcab5eb48250ee7d0c7896b51a2c5eec99c1feea5f32025635f5ae4b00070"
"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30"
"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
"checksum persistent 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8e8fa0009c4f3d350281309909c618abddf10bb7e3145f28410782f6a5ec74c5"
"checksum phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cb325642290f28ee14d8c6201159949a872f220c62af6e110a56ea914fbe42fc"
"checksum phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d62594c0bb54c464f633175d502038177e90309daf2e0158be42ed5f023ce88f"
"checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03"
"checksum phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "07e24b0ca9643bdecd0632f2b3da6b1b89bbb0030e0b992afc1113b23a7bc2f2"
"checksum plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1a6a0dc3910bc8db877ffed8e457763b317cf880df4ae19109b9f77d277cf6e0"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum rand 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "9e7944d95d25ace8f377da3ac7068ce517e4c646754c43a1b1849177bbf72e59"
"checksum redox_syscall 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)" = "07b8f011e3254d5a9b318fde596d409a0001c9ae4c6e7907520c2eaa4d988c99"
"checksum regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "744554e01ccbd98fff8c457c3b092cd67af62a555a43bfe97ae8a0451f7799fa"
"checksum regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8e931c58b93d86f080c734bfd2bce7dd0079ae2331235818133c8be7f422e20e"
"checksum relay 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f301bafeb60867c85170031bdb2fcf24c8041f33aee09e7b116a58d4e9f781c5"
"checksum route-recognizer 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3255338088df8146ba63d60a9b8e3556f1146ce2973bc05a75181a42ce2256"
"checksum router 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dc63b6f3b8895b0d04e816b2b1aa58fdba2d5acca3cbb8f0ab8e017347d57397"
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
"checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f"
"checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d"
"checksum serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "db99f3919e20faa51bb2996057f5031d8685019b5a06139b1ce761da671b8526"
"checksum serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "f4ba7591cfe93755e89eeecdbcc668885624829b020050e6aec99c2a03bd3fd0"
"checksum serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6e03f1c9530c3fb0a0a5c9b826bdd9246a5921ae995d75f512ac917fc4dd55b5"
"checksum serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c9db7266c7d63a4c4b7fe8719656ccdd51acf1bed6124b174f933b009fb10bcb"
"checksum serde_yaml 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e0f868d400d9d13d00988da49f7f02aeac6ef00f11901a8c535bd59d777b9e19"
"checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537"
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
"checksum slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdeff4cd9ecff59ec7e3744cbca73dfe5ac35c2aedb2cfba8a1c715a18912e9d"
"checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013"
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
"checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5"
"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963"
"checksum time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "d5d788d3aa77bc0ef3e9621256885555368b47bd495c13dd2e7413c89f845520"
"checksum tokio-core 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c87c27560184212c9dc45cd8f38623f37918248aad5b58fb65303b5d07a98c6e"
"checksum tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "514aae203178929dbf03318ad7c683126672d4d96eccb77b29603d33c9e25743"
"checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389"
"checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162"
"checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079"
"checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887"
"checksum typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6"
"checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33"
"checksum unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284b6d3db520d67fbe88fd778c21510d1b0ba4a551e5d0fbb023d33405f6de8a"
"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
"checksum unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f"
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
"checksum unsafe-any 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f30360d7979f5e9c6e6cea48af192ea8fab4afb3cf72597154b8f08935bc9c7f"
"checksum url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa35e768d4daf1d85733418a49fb42e10d7f633e394fccab4ab7aba897053fe2"
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
"checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d"
"checksum virtual-kubelet-adapter 0.1.0 (git+https://github.com/avranju/rust-virtual-kubelet-adapter?rev=4250103d31e2864725e47bdd23295e79ee12b6d0)" = "<none>"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
"checksum yaml-rust 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57ab38ee1a4a266ed033496cf9af1828d8d6e6c1cfa5f643a2809effcae4d628"

View File

@@ -0,0 +1,11 @@
[package]
name = "rust-web-provider"
version = "0.1.0"
authors = ["Rajasekharan Vengalil <rajave@microsoft.com>"]
[dependencies]
kube_rust = { git = "https://github.com/avranju/kube-rust", rev = "058de6366d0d75cb60b2d0fd5ba1abd2e7d83fff" }
virtual-kubelet-adapter = { git = "https://github.com/avranju/rust-virtual-kubelet-adapter", rev = "4250103d31e2864725e47bdd23295e79ee12b6d0"}
log = "0.4"
env_logger = "0.4"
time = "0.1"

View File

@@ -0,0 +1,9 @@
FROM debian:stretch-slim
WORKDIR /app
ADD ./rust-web-provider /app/rust-web-provider
ENV RUST_LOG=info
EXPOSE 3000
ENTRYPOINT [ "/app/rust-web-provider" ]

View File

@@ -0,0 +1,75 @@
#!/bin/bash
set -e
SCRIPT_NAME=$(basename "$0")
DIR=$(cd "$(dirname "$0")" && pwd)
ROOT_FOLDER="$DIR/.."
PUBLISH_DIR=$ROOT_FOLDER/target/publish
TARGET_NAME=rust-web-provider
IMAGE_NAME=rust-web-provider
IMAGE_VERSION=latest
BUILD_RELEASE=true
SOURCE_RELEASE_DIR=$ROOT_FOLDER/target/release
SOURCE_DEBUG_DIR=$ROOT_FOLDER/target/debug
SOURCE_DIR=$SOURCE_RELEASE_DIR
usage()
{
echo "$SCRIPT_NAME [options]"
echo "Note: You might have to run this as root or sudo."
echo ""
echo "options"
echo " -i, --image-name Image name (default: rust-web-provider)"
echo " -v, --image-version Docker Image Version (default: latest)"
echo " -r, --build-release Build release configuration - true|false (default: true)"
exit 1;
}
process_args()
{
save_next_arg=0
for arg in "$@"
do
if [ $save_next_arg -eq 1 ]; then
IMAGE_NAME="$arg"
save_next_arg=0
elif [ $save_next_arg -eq 2 ]; then
IMAGE_VERSION="$arg"
save_next_arg=0
elif [ $save_next_arg -eq 3 ]; then
BUILD_RELEASE="$arg"
save_next_arg=0
else
case "$arg" in
"-h" | "--help" ) usage;;
"-i" | "--image-name" ) save_next_arg=1;;
"-v" | "--image-version" ) save_next_arg=2;;
"-r" | "--build-release" ) save_next_arg=3;;
* ) usage;;
esac
fi
done
}
# process command line args
process_args "$@"
# build bits
if [ "$BUILD_RELEASE" == "true" ]; then
cargo build --release
else
SOURCE_DIR=$SOURCE_DEBUG_DIR
cargo build
fi
# copy release binary & Dockerfile to a "publish" folder
rm -rf "$PUBLISH_DIR"
mkdir -p "$PUBLISH_DIR"
cp "$ROOT_FOLDER/Dockerfile" "$PUBLISH_DIR"
cp "$SOURCE_DIR/$TARGET_NAME" "$PUBLISH_DIR"
# build the Docker image
pushd "$PUBLISH_DIR"
docker build -t "$IMAGE_NAME":"$IMAGE_VERSION" .
popd

View File

@@ -0,0 +1,20 @@
extern crate env_logger;
extern crate kube_rust;
#[macro_use]
extern crate log;
extern crate time;
extern crate virtual_kubelet_adapter;
mod utils;
mod unit_provider;
use virtual_kubelet_adapter::start_server;
use unit_provider::UnitProvider;
fn main() {
// initialize logger
env_logger::init().unwrap();
let provider = Box::new(UnitProvider::new());
start_server(provider).unwrap();
}

View File

@@ -0,0 +1,156 @@
use kube_rust::models::{V1NodeAddress, V1NodeCondition, V1NodeDaemonEndpoints, V1Pod, V1PodStatus};
use virtual_kubelet_adapter::{Error, Provider, Result};
use std::collections::BTreeMap;
use utils::Filter;
use time;
pub struct UnitProvider {
pods_map: BTreeMap<String, V1Pod>,
}
impl UnitProvider {
pub fn new() -> UnitProvider {
UnitProvider {
pods_map: BTreeMap::new(),
}
}
fn make_pod_id(&self, pod: &V1Pod) -> String {
let empty = String::from("");
pod.metadata()
.map(|m| m.name().unwrap_or(&empty))
.unwrap_or(&empty)
.clone()
}
fn pod_name(&mut self, pod: &V1Pod) -> String {
let empty = "".to_owned();
format!(
"{}",
pod.metadata()
.map(|m| m.name())
.and_then(|s| s)
.unwrap_or(&empty)
)
}
}
impl Provider for UnitProvider {
fn create_pod(&mut self, pod: &V1Pod) -> Result<()> {
info!("Creating pod: {}", self.pod_name(pod));
let id = self.make_pod_id(pod);
let mut new_pod = pod.clone();
new_pod.set_status(
pod.status()
.map(|s| s.clone())
.unwrap_or_else(|| V1PodStatus::new())
.with_phase(String::from("Running")),
);
self.pods_map.insert(id, new_pod);
Ok(())
}
fn update_pod(&mut self, pod: &V1Pod) -> Result<()> {
info!("Updating pod: {}", self.pod_name(pod));
// update the pod definition if it exists
let id = self.make_pod_id(pod);
if self.pods_map.contains_key(&id) {
self.pods_map.insert(id, pod.clone());
}
Ok(())
}
fn delete_pod(&mut self, pod: &V1Pod) -> Result<()> {
info!("Deleting pod: {}", self.pod_name(pod));
let id = self.make_pod_id(pod);
self.pods_map.remove(&id);
Ok(())
}
fn get_pod(&self, namespace: &str, name: &str) -> Result<V1Pod> {
info!("Getting pod: {}", name);
self.pods_map
.get(name)
.filter(|pod| {
let empty = String::from("");
let ns = pod.metadata()
.map(|m| m.namespace())
.and_then(|n| n)
.unwrap_or(&empty);
namespace.len() == 0 || namespace == ns
})
.map(|pod| pod.clone())
.ok_or_else(|| {
info!("Could not find pod: {}", name);
Error::not_found("Pod not found")
})
}
fn get_container_logs(
&self,
namespace: &str,
pod_name: &str,
container_name: &str,
tail: i32,
) -> Result<String> {
Ok(format!(
"get_container_logs() - ns: {}, pod_name: {}, container_name: {}, tail: {}",
namespace, pod_name, container_name, tail
))
}
fn get_pod_status(&self, _: &str, name: &str) -> Result<V1PodStatus> {
info!("Getting pod status: {}", name);
self.pods_map
.get(name)
.map(|pod| pod.status())
.and_then(|pod_status| pod_status)
.map(|pod_status| pod_status.clone())
.ok_or_else(|| {
info!("Could not find pod/status: {}", name);
Error::not_found("Pod/status not found")
})
}
fn get_pods(&self) -> Result<Vec<V1Pod>> {
info!("Getting pods");
Ok(self.pods_map.values().cloned().collect())
}
fn capacity(&self) -> Result<BTreeMap<String, String>> {
info!("Getting capacity");
let values = [("cpu", "20"), ("memory", "100Gi"), ("pods", "20")];
let mut map = BTreeMap::new();
for v in values.iter() {
map.insert(v.0.to_string(), v.1.to_string());
}
Ok(map)
}
fn node_conditions(&self) -> Result<Vec<V1NodeCondition>> {
info!("Getting node_condition");
Ok(vec![
V1NodeCondition::new(String::from("True"), String::from("Ready"))
.with_reason(String::from("KubeletReady"))
.with_message(String::from("Rusty times."))
.with_last_heartbeat_time(format!("{}", time::now_utc().rfc3339()))
.with_last_transition_time(format!("{}", time::now_utc().rfc3339())),
])
}
fn node_addresses(&self) -> Result<Vec<V1NodeAddress>> {
Ok(vec![])
}
fn node_daemon_endpoints(&self) -> Result<V1NodeDaemonEndpoints> {
Err(Error::new("Not implemented"))
}
fn operating_system(&self) -> String {
String::from("linux")
}
}

View File

@@ -0,0 +1,18 @@
pub trait Filter<T> {
fn filter<P: FnOnce(&T) -> bool>(self, predicate: P) -> Self;
}
impl<T> Filter<T> for Option<T> {
fn filter<P: FnOnce(&T) -> bool>(self, predicate: P) -> Self {
match self {
Some(x) => {
if predicate(&x) {
Some(x)
} else {
None
}
}
None => None,
}
}
}

22
vendor/github.com/cenkalti/backoff/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,22 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe

9
vendor/github.com/cenkalti/backoff/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,9 @@
language: go
go:
- 1.3.3
- tip
before_install:
- go get github.com/mattn/goveralls
- go get golang.org/x/tools/cmd/cover
script:
- $HOME/gopath/bin/goveralls -service=travis-ci

20
vendor/github.com/cenkalti/backoff/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2014 Cenk Altı
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

30
vendor/github.com/cenkalti/backoff/README.md generated vendored Normal file
View File

@@ -0,0 +1,30 @@
# Exponential Backoff [![GoDoc][godoc image]][godoc] [![Build Status][travis image]][travis] [![Coverage Status][coveralls image]][coveralls]
This is a Go port of the exponential backoff algorithm from [Google's HTTP Client Library for Java][google-http-java-client].
[Exponential backoff][exponential backoff wiki]
is an algorithm that uses feedback to multiplicatively decrease the rate of some process,
in order to gradually find an acceptable rate.
The retries exponentially increase and stop increasing when a certain threshold is met.
## Usage
See https://godoc.org/github.com/cenkalti/backoff#pkg-examples
## Contributing
* I would like to keep this library as small as possible.
* Please don't send a PR without opening an issue and discussing it first.
* If proposed change is not a common use case, I will probably not accept it.
[godoc]: https://godoc.org/github.com/cenkalti/backoff
[godoc image]: https://godoc.org/github.com/cenkalti/backoff?status.png
[travis]: https://travis-ci.org/cenkalti/backoff
[travis image]: https://travis-ci.org/cenkalti/backoff.png?branch=master
[coveralls]: https://coveralls.io/github/cenkalti/backoff?branch=master
[coveralls image]: https://coveralls.io/repos/github/cenkalti/backoff/badge.svg?branch=master
[google-http-java-client]: https://github.com/google/google-http-java-client
[exponential backoff wiki]: http://en.wikipedia.org/wiki/Exponential_backoff
[advanced example]: https://godoc.org/github.com/cenkalti/backoff#example_

66
vendor/github.com/cenkalti/backoff/backoff.go generated vendored Normal file
View File

@@ -0,0 +1,66 @@
// Package backoff implements backoff algorithms for retrying operations.
//
// Use Retry function for retrying operations that may fail.
// If Retry does not meet your needs,
// copy/paste the function into your project and modify as you wish.
//
// There is also Ticker type similar to time.Ticker.
// You can use it if you need to work with channels.
//
// See Examples section below for usage examples.
package backoff
import "time"
// BackOff is a backoff policy for retrying an operation.
type BackOff interface {
// NextBackOff returns the duration to wait before retrying the operation,
// or backoff.Stop to indicate that no more retries should be made.
//
// Example usage:
//
// duration := backoff.NextBackOff();
// if (duration == backoff.Stop) {
// // Do not retry operation.
// } else {
// // Sleep for duration and retry operation.
// }
//
NextBackOff() time.Duration
// Reset to initial state.
Reset()
}
// Stop indicates that no more retries should be made for use in NextBackOff().
const Stop time.Duration = -1
// ZeroBackOff is a fixed backoff policy whose backoff time is always zero,
// meaning that the operation is retried immediately without waiting, indefinitely.
type ZeroBackOff struct{}
func (b *ZeroBackOff) Reset() {}
func (b *ZeroBackOff) NextBackOff() time.Duration { return 0 }
// StopBackOff is a fixed backoff policy that always returns backoff.Stop for
// NextBackOff(), meaning that the operation should never be retried.
type StopBackOff struct{}
func (b *StopBackOff) Reset() {}
func (b *StopBackOff) NextBackOff() time.Duration { return Stop }
// ConstantBackOff is a backoff policy that always returns the same backoff delay.
// This is in contrast to an exponential backoff policy,
// which returns a delay that grows longer as you call NextBackOff() over and over again.
type ConstantBackOff struct {
Interval time.Duration
}
func (b *ConstantBackOff) Reset() {}
func (b *ConstantBackOff) NextBackOff() time.Duration { return b.Interval }
func NewConstantBackOff(d time.Duration) *ConstantBackOff {
return &ConstantBackOff{Interval: d}
}

27
vendor/github.com/cenkalti/backoff/backoff_test.go generated vendored Normal file
View File

@@ -0,0 +1,27 @@
package backoff
import (
"testing"
"time"
)
func TestNextBackOffMillis(t *testing.T) {
subtestNextBackOff(t, 0, new(ZeroBackOff))
subtestNextBackOff(t, Stop, new(StopBackOff))
}
func subtestNextBackOff(t *testing.T, expectedValue time.Duration, backOffPolicy BackOff) {
for i := 0; i < 10; i++ {
next := backOffPolicy.NextBackOff()
if next != expectedValue {
t.Errorf("got: %d expected: %d", next, expectedValue)
}
}
}
func TestConstantBackOff(t *testing.T) {
backoff := NewConstantBackOff(time.Second)
if backoff.NextBackOff() != time.Second {
t.Error("invalid interval")
}
}

60
vendor/github.com/cenkalti/backoff/context.go generated vendored Normal file
View File

@@ -0,0 +1,60 @@
package backoff
import (
"time"
"golang.org/x/net/context"
)
// BackOffContext is a backoff policy that stops retrying after the context
// is canceled.
type BackOffContext interface {
BackOff
Context() context.Context
}
type backOffContext struct {
BackOff
ctx context.Context
}
// WithContext returns a BackOffContext with context ctx
//
// ctx must not be nil
func WithContext(b BackOff, ctx context.Context) BackOffContext {
if ctx == nil {
panic("nil context")
}
if b, ok := b.(*backOffContext); ok {
return &backOffContext{
BackOff: b.BackOff,
ctx: ctx,
}
}
return &backOffContext{
BackOff: b,
ctx: ctx,
}
}
func ensureContext(b BackOff) BackOffContext {
if cb, ok := b.(BackOffContext); ok {
return cb
}
return WithContext(b, context.Background())
}
func (b *backOffContext) Context() context.Context {
return b.ctx
}
func (b *backOffContext) NextBackOff() time.Duration {
select {
case <-b.Context().Done():
return Stop
default:
return b.BackOff.NextBackOff()
}
}

26
vendor/github.com/cenkalti/backoff/context_test.go generated vendored Normal file
View File

@@ -0,0 +1,26 @@
package backoff
import (
"testing"
"time"
"golang.org/x/net/context"
)
func TestContext(t *testing.T) {
b := NewConstantBackOff(time.Millisecond)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
cb := WithContext(b, ctx)
if cb.Context() != ctx {
t.Error("invalid context")
}
cancel()
if cb.NextBackOff() != Stop {
t.Error("invalid next back off")
}
}

73
vendor/github.com/cenkalti/backoff/example_test.go generated vendored Normal file
View File

@@ -0,0 +1,73 @@
package backoff
import (
"log"
"golang.org/x/net/context"
)
func ExampleRetry() {
// An operation that may fail.
operation := func() error {
return nil // or an error
}
err := Retry(operation, NewExponentialBackOff())
if err != nil {
// Handle error.
return
}
// Operation is successful.
}
func ExampleRetryContext() {
// A context
ctx := context.Background()
// An operation that may fail.
operation := func() error {
return nil // or an error
}
b := WithContext(NewExponentialBackOff(), ctx)
err := Retry(operation, b)
if err != nil {
// Handle error.
return
}
// Operation is successful.
}
func ExampleTicker() {
// An operation that may fail.
operation := func() error {
return nil // or an error
}
ticker := NewTicker(NewExponentialBackOff())
var err error
// Ticks will continue to arrive when the previous operation is still running,
// so operations that take a while to fail could run in quick succession.
for _ = range ticker.C {
if err = operation(); err != nil {
log.Println(err, "will retry...")
continue
}
ticker.Stop()
break
}
if err != nil {
// Operation has failed.
return
}
// Operation is successful.
return
}

156
vendor/github.com/cenkalti/backoff/exponential.go generated vendored Normal file
View File

@@ -0,0 +1,156 @@
package backoff
import (
"math/rand"
"time"
)
/*
ExponentialBackOff is a backoff implementation that increases the backoff
period for each retry attempt using a randomization function that grows exponentially.
NextBackOff() is calculated using the following formula:
randomized interval =
RetryInterval * (random value in range [1 - RandomizationFactor, 1 + RandomizationFactor])
In other words NextBackOff() will range between the randomization factor
percentage below and above the retry interval.
For example, given the following parameters:
RetryInterval = 2
RandomizationFactor = 0.5
Multiplier = 2
the actual backoff period used in the next retry attempt will range between 1 and 3 seconds,
multiplied by the exponential, that is, between 2 and 6 seconds.
Note: MaxInterval caps the RetryInterval and not the randomized interval.
If the time elapsed since an ExponentialBackOff instance is created goes past the
MaxElapsedTime, then the method NextBackOff() starts returning backoff.Stop.
The elapsed time can be reset by calling Reset().
Example: Given the following default arguments, for 10 tries the sequence will be,
and assuming we go over the MaxElapsedTime on the 10th try:
Request # RetryInterval (seconds) Randomized Interval (seconds)
1 0.5 [0.25, 0.75]
2 0.75 [0.375, 1.125]
3 1.125 [0.562, 1.687]
4 1.687 [0.8435, 2.53]
5 2.53 [1.265, 3.795]
6 3.795 [1.897, 5.692]
7 5.692 [2.846, 8.538]
8 8.538 [4.269, 12.807]
9 12.807 [6.403, 19.210]
10 19.210 backoff.Stop
Note: Implementation is not thread-safe.
*/
type ExponentialBackOff struct {
InitialInterval time.Duration
RandomizationFactor float64
Multiplier float64
MaxInterval time.Duration
// After MaxElapsedTime the ExponentialBackOff stops.
// It never stops if MaxElapsedTime == 0.
MaxElapsedTime time.Duration
Clock Clock
currentInterval time.Duration
startTime time.Time
random *rand.Rand
}
// Clock is an interface that returns current time for BackOff.
type Clock interface {
Now() time.Time
}
// Default values for ExponentialBackOff.
const (
DefaultInitialInterval = 500 * time.Millisecond
DefaultRandomizationFactor = 0.5
DefaultMultiplier = 1.5
DefaultMaxInterval = 60 * time.Second
DefaultMaxElapsedTime = 15 * time.Minute
)
// NewExponentialBackOff creates an instance of ExponentialBackOff using default values.
func NewExponentialBackOff() *ExponentialBackOff {
b := &ExponentialBackOff{
InitialInterval: DefaultInitialInterval,
RandomizationFactor: DefaultRandomizationFactor,
Multiplier: DefaultMultiplier,
MaxInterval: DefaultMaxInterval,
MaxElapsedTime: DefaultMaxElapsedTime,
Clock: SystemClock,
random: rand.New(rand.NewSource(time.Now().UnixNano())),
}
b.Reset()
return b
}
type systemClock struct{}
func (t systemClock) Now() time.Time {
return time.Now()
}
// SystemClock implements Clock interface that uses time.Now().
var SystemClock = systemClock{}
// Reset the interval back to the initial retry interval and restarts the timer.
func (b *ExponentialBackOff) Reset() {
b.currentInterval = b.InitialInterval
b.startTime = b.Clock.Now()
}
// NextBackOff calculates the next backoff interval using the formula:
// Randomized interval = RetryInterval +/- (RandomizationFactor * RetryInterval)
func (b *ExponentialBackOff) NextBackOff() time.Duration {
// Make sure we have not gone over the maximum elapsed time.
if b.MaxElapsedTime != 0 && b.GetElapsedTime() > b.MaxElapsedTime {
return Stop
}
defer b.incrementCurrentInterval()
if b.random == nil {
b.random = rand.New(rand.NewSource(time.Now().UnixNano()))
}
return getRandomValueFromInterval(b.RandomizationFactor, b.random.Float64(), b.currentInterval)
}
// GetElapsedTime returns the elapsed time since an ExponentialBackOff instance
// is created and is reset when Reset() is called.
//
// The elapsed time is computed using time.Now().UnixNano().
func (b *ExponentialBackOff) GetElapsedTime() time.Duration {
return b.Clock.Now().Sub(b.startTime)
}
// Increments the current interval by multiplying it with the multiplier.
func (b *ExponentialBackOff) incrementCurrentInterval() {
// Check for overflow, if overflow is detected set the current interval to the max interval.
if float64(b.currentInterval) >= float64(b.MaxInterval)/b.Multiplier {
b.currentInterval = b.MaxInterval
} else {
b.currentInterval = time.Duration(float64(b.currentInterval) * b.Multiplier)
}
}
// Returns a random value from the following interval:
// [randomizationFactor * currentInterval, randomizationFactor * currentInterval].
func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration {
var delta = randomizationFactor * float64(currentInterval)
var minInterval = float64(currentInterval) - delta
var maxInterval = float64(currentInterval) + delta
// Get a random value from the range [minInterval, maxInterval].
// The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then
// we want a 33% chance for selecting either 1, 2 or 3.
return time.Duration(minInterval + (random * (maxInterval - minInterval + 1)))
}

108
vendor/github.com/cenkalti/backoff/exponential_test.go generated vendored Normal file
View File

@@ -0,0 +1,108 @@
package backoff
import (
"math"
"testing"
"time"
)
func TestBackOff(t *testing.T) {
var (
testInitialInterval = 500 * time.Millisecond
testRandomizationFactor = 0.1
testMultiplier = 2.0
testMaxInterval = 5 * time.Second
testMaxElapsedTime = 15 * time.Minute
)
exp := NewExponentialBackOff()
exp.InitialInterval = testInitialInterval
exp.RandomizationFactor = testRandomizationFactor
exp.Multiplier = testMultiplier
exp.MaxInterval = testMaxInterval
exp.MaxElapsedTime = testMaxElapsedTime
exp.Reset()
var expectedResults = []time.Duration{500, 1000, 2000, 4000, 5000, 5000, 5000, 5000, 5000, 5000}
for i, d := range expectedResults {
expectedResults[i] = d * time.Millisecond
}
for _, expected := range expectedResults {
assertEquals(t, expected, exp.currentInterval)
// Assert that the next backoff falls in the expected range.
var minInterval = expected - time.Duration(testRandomizationFactor*float64(expected))
var maxInterval = expected + time.Duration(testRandomizationFactor*float64(expected))
var actualInterval = exp.NextBackOff()
if !(minInterval <= actualInterval && actualInterval <= maxInterval) {
t.Error("error")
}
}
}
func TestGetRandomizedInterval(t *testing.T) {
// 33% chance of being 1.
assertEquals(t, 1, getRandomValueFromInterval(0.5, 0, 2))
assertEquals(t, 1, getRandomValueFromInterval(0.5, 0.33, 2))
// 33% chance of being 2.
assertEquals(t, 2, getRandomValueFromInterval(0.5, 0.34, 2))
assertEquals(t, 2, getRandomValueFromInterval(0.5, 0.66, 2))
// 33% chance of being 3.
assertEquals(t, 3, getRandomValueFromInterval(0.5, 0.67, 2))
assertEquals(t, 3, getRandomValueFromInterval(0.5, 0.99, 2))
}
type TestClock struct {
i time.Duration
start time.Time
}
func (c *TestClock) Now() time.Time {
t := c.start.Add(c.i)
c.i += time.Second
return t
}
func TestGetElapsedTime(t *testing.T) {
var exp = NewExponentialBackOff()
exp.Clock = &TestClock{}
exp.Reset()
var elapsedTime = exp.GetElapsedTime()
if elapsedTime != time.Second {
t.Errorf("elapsedTime=%d", elapsedTime)
}
}
func TestMaxElapsedTime(t *testing.T) {
var exp = NewExponentialBackOff()
exp.Clock = &TestClock{start: time.Time{}.Add(10000 * time.Second)}
// Change the currentElapsedTime to be 0 ensuring that the elapsed time will be greater
// than the max elapsed time.
exp.startTime = time.Time{}
assertEquals(t, Stop, exp.NextBackOff())
}
func TestBackOffOverflow(t *testing.T) {
var (
testInitialInterval time.Duration = math.MaxInt64 / 2
testMaxInterval time.Duration = math.MaxInt64
testMultiplier = 2.1
)
exp := NewExponentialBackOff()
exp.InitialInterval = testInitialInterval
exp.Multiplier = testMultiplier
exp.MaxInterval = testMaxInterval
exp.Reset()
exp.NextBackOff()
// Assert that when an overflow is possible the current varerval time.Duration is set to the max varerval time.Duration .
assertEquals(t, testMaxInterval, exp.currentInterval)
}
func assertEquals(t *testing.T, expected, value time.Duration) {
if expected != value {
t.Errorf("got: %d, expected: %d", value, expected)
}
}

78
vendor/github.com/cenkalti/backoff/retry.go generated vendored Normal file
View File

@@ -0,0 +1,78 @@
package backoff
import "time"
// An Operation is executing by Retry() or RetryNotify().
// The operation will be retried using a backoff policy if it returns an error.
type Operation func() error
// Notify is a notify-on-error function. It receives an operation error and
// backoff delay if the operation failed (with an error).
//
// NOTE that if the backoff policy stated to stop retrying,
// the notify function isn't called.
type Notify func(error, time.Duration)
// Retry the operation o until it does not return error or BackOff stops.
// o is guaranteed to be run at least once.
// It is the caller's responsibility to reset b after Retry returns.
//
// If o returns a *PermanentError, the operation is not retried, and the
// wrapped error is returned.
//
// Retry sleeps the goroutine for the duration returned by BackOff after a
// failed operation returns.
func Retry(o Operation, b BackOff) error { return RetryNotify(o, b, nil) }
// RetryNotify calls notify function with the error and wait duration
// for each failed attempt before sleep.
func RetryNotify(operation Operation, b BackOff, notify Notify) error {
var err error
var next time.Duration
cb := ensureContext(b)
b.Reset()
for {
if err = operation(); err == nil {
return nil
}
if permanent, ok := err.(*PermanentError); ok {
return permanent.Err
}
if next = b.NextBackOff(); next == Stop {
return err
}
if notify != nil {
notify(err, next)
}
t := time.NewTimer(next)
select {
case <-cb.Context().Done():
t.Stop()
return err
case <-t.C:
}
}
}
// PermanentError signals that the operation should not be retried.
type PermanentError struct {
Err error
}
func (e *PermanentError) Error() string {
return e.Err.Error()
}
// Permanent wraps the given err in a *PermanentError.
func Permanent(err error) *PermanentError {
return &PermanentError{
Err: err,
}
}

99
vendor/github.com/cenkalti/backoff/retry_test.go generated vendored Normal file
View File

@@ -0,0 +1,99 @@
package backoff
import (
"errors"
"fmt"
"log"
"testing"
"time"
"golang.org/x/net/context"
)
func TestRetry(t *testing.T) {
const successOn = 3
var i = 0
// This function is successful on "successOn" calls.
f := func() error {
i++
log.Printf("function is called %d. time\n", i)
if i == successOn {
log.Println("OK")
return nil
}
log.Println("error")
return errors.New("error")
}
err := Retry(f, NewExponentialBackOff())
if err != nil {
t.Errorf("unexpected error: %s", err.Error())
}
if i != successOn {
t.Errorf("invalid number of retries: %d", i)
}
}
func TestRetryContext(t *testing.T) {
var cancelOn = 3
var i = 0
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// This function cancels context on "cancelOn" calls.
f := func() error {
i++
log.Printf("function is called %d. time\n", i)
// cancelling the context in the operation function is not a typical
// use-case, however it allows to get predictable test results.
if i == cancelOn {
cancel()
}
log.Println("error")
return fmt.Errorf("error (%d)", i)
}
err := Retry(f, WithContext(NewConstantBackOff(time.Millisecond), ctx))
if err == nil {
t.Errorf("error is unexpectedly nil")
}
if err.Error() != "error (3)" {
t.Errorf("unexpected error: %s", err.Error())
}
if i != cancelOn {
t.Errorf("invalid number of retries: %d", i)
}
}
func TestRetryPermenent(t *testing.T) {
const permanentOn = 3
var i = 0
// This function fails permanently after permanentOn tries
f := func() error {
i++
log.Printf("function is called %d. time\n", i)
if i == permanentOn {
log.Println("permanent error")
return Permanent(errors.New("permanent error"))
}
log.Println("error")
return errors.New("error")
}
err := Retry(f, NewExponentialBackOff())
if err == nil || err.Error() != "permanent error" {
t.Errorf("unexpected error: %s", err)
}
if i != permanentOn {
t.Errorf("invalid number of retries: %d", i)
}
}

81
vendor/github.com/cenkalti/backoff/ticker.go generated vendored Normal file
View File

@@ -0,0 +1,81 @@
package backoff
import (
"runtime"
"sync"
"time"
)
// Ticker holds a channel that delivers `ticks' of a clock at times reported by a BackOff.
//
// Ticks will continue to arrive when the previous operation is still running,
// so operations that take a while to fail could run in quick succession.
type Ticker struct {
C <-chan time.Time
c chan time.Time
b BackOffContext
stop chan struct{}
stopOnce sync.Once
}
// NewTicker returns a new Ticker containing a channel that will send the time at times
// specified by the BackOff argument. Ticker is guaranteed to tick at least once.
// The channel is closed when Stop method is called or BackOff stops.
func NewTicker(b BackOff) *Ticker {
c := make(chan time.Time)
t := &Ticker{
C: c,
c: c,
b: ensureContext(b),
stop: make(chan struct{}),
}
go t.run()
runtime.SetFinalizer(t, (*Ticker).Stop)
return t
}
// Stop turns off a ticker. After Stop, no more ticks will be sent.
func (t *Ticker) Stop() {
t.stopOnce.Do(func() { close(t.stop) })
}
func (t *Ticker) run() {
c := t.c
defer close(c)
t.b.Reset()
// Ticker is guaranteed to tick at least once.
afterC := t.send(time.Now())
for {
if afterC == nil {
return
}
select {
case tick := <-afterC:
afterC = t.send(tick)
case <-t.stop:
t.c = nil // Prevent future ticks from being sent to the channel.
return
case <-t.b.Context().Done():
return
}
}
}
func (t *Ticker) send(tick time.Time) <-chan time.Time {
select {
case t.c <- tick:
case <-t.stop:
return nil
}
next := t.b.NextBackOff()
if next == Stop {
t.Stop()
return nil
}
return time.After(next)
}

94
vendor/github.com/cenkalti/backoff/ticker_test.go generated vendored Normal file
View File

@@ -0,0 +1,94 @@
package backoff
import (
"errors"
"fmt"
"log"
"testing"
"time"
"golang.org/x/net/context"
)
func TestTicker(t *testing.T) {
const successOn = 3
var i = 0
// This function is successful on "successOn" calls.
f := func() error {
i++
log.Printf("function is called %d. time\n", i)
if i == successOn {
log.Println("OK")
return nil
}
log.Println("error")
return errors.New("error")
}
b := NewExponentialBackOff()
ticker := NewTicker(b)
var err error
for _ = range ticker.C {
if err = f(); err != nil {
t.Log(err)
continue
}
break
}
if err != nil {
t.Errorf("unexpected error: %s", err.Error())
}
if i != successOn {
t.Errorf("invalid number of retries: %d", i)
}
}
func TestTickerContext(t *testing.T) {
const cancelOn = 3
var i = 0
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// This function cancels context on "cancelOn" calls.
f := func() error {
i++
log.Printf("function is called %d. time\n", i)
// cancelling the context in the operation function is not a typical
// use-case, however it allows to get predictable test results.
if i == cancelOn {
cancel()
}
log.Println("error")
return fmt.Errorf("error (%d)", i)
}
b := WithContext(NewConstantBackOff(time.Millisecond), ctx)
ticker := NewTicker(b)
var err error
for _ = range ticker.C {
if err = f(); err != nil {
t.Log(err)
continue
}
break
}
if err == nil {
t.Errorf("error is unexpectedly nil")
}
if err.Error() != "error (3)" {
t.Errorf("unexpected error: %s", err.Error())
}
if i != cancelOn {
t.Errorf("invalid number of retries: %d", i)
}
}

35
vendor/github.com/cenkalti/backoff/tries.go generated vendored Normal file
View File

@@ -0,0 +1,35 @@
package backoff
import "time"
/*
WithMaxTries creates a wrapper around another BackOff, which will
return Stop if NextBackOff() has been called too many times since
the last time Reset() was called
Note: Implementation is not thread-safe.
*/
func WithMaxTries(b BackOff, max uint64) BackOff {
return &backOffTries{delegate: b, maxTries: max}
}
type backOffTries struct {
delegate BackOff
maxTries uint64
numTries uint64
}
func (b *backOffTries) NextBackOff() time.Duration {
if b.maxTries > 0 {
if b.maxTries <= b.numTries {
return Stop
}
b.numTries++
}
return b.delegate.NextBackOff()
}
func (b *backOffTries) Reset() {
b.numTries = 0
b.delegate.Reset()
}

55
vendor/github.com/cenkalti/backoff/tries_test.go generated vendored Normal file
View File

@@ -0,0 +1,55 @@
package backoff
import (
"math/rand"
"testing"
"time"
)
func TestMaxTriesHappy(t *testing.T) {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
max := 17 + r.Intn(13)
bo := WithMaxTries(&ZeroBackOff{}, uint64(max))
// Load up the tries count, but reset should clear the record
for ix := 0; ix < max/2; ix++ {
bo.NextBackOff()
}
bo.Reset()
// Now fill the tries count all the way up
for ix := 0; ix < max; ix++ {
d := bo.NextBackOff()
if d == Stop {
t.Errorf("returned Stop on try %d", ix)
}
}
// We have now called the BackOff max number of times, we expect
// the next result to be Stop, even if we try it multiple times
for ix := 0; ix < 7; ix++ {
d := bo.NextBackOff()
if d != Stop {
t.Error("invalid next back off")
}
}
// Reset makes it all work again
bo.Reset()
d := bo.NextBackOff()
if d == Stop {
t.Error("returned Stop after reset")
}
}
func TestMaxTriesZero(t *testing.T) {
// It might not make sense, but its okay to send a zero
bo := WithMaxTries(&ZeroBackOff{}, uint64(0))
for ix := 0; ix < 11; ix++ {
d := bo.NextBackOff()
if d == Stop {
t.Errorf("returned Stop on try %d", ix)
}
}
}

View File

@@ -3,12 +3,14 @@ package vkubelet
import (
"github.com/virtual-kubelet/virtual-kubelet/providers/azure"
"github.com/virtual-kubelet/virtual-kubelet/providers/hypersh"
"github.com/virtual-kubelet/virtual-kubelet/providers/web"
"k8s.io/api/core/v1"
)
// Compile time proof that our implementations meet the Provider interface.
var _ Provider = (*azure.ACIProvider)(nil)
var _ Provider = (*hypersh.HyperProvider)(nil)
var _ Provider = (*web.BrokerProvider)(nil)
// Provider contains the methods required to implement a virtual-kubelet provider.
type Provider interface {
@@ -37,15 +39,15 @@ type Provider interface {
Capacity() v1.ResourceList
// NodeConditions returns a list of conditions (Ready, OutOfDisk, etc), which is polled periodically to update the node status
// within Kuberentes.
// within Kubernetes.
NodeConditions() []v1.NodeCondition
// NodeAddresses returns a list of addresses for the node status
// within Kuberentes.
// within Kubernetes.
NodeAddresses() []v1.NodeAddress
// NodeDaemonEndpoints returns NodeDaemonEndpoints for the node status
// within Kuberentes.
// within Kubernetes.
NodeDaemonEndpoints() *v1.NodeDaemonEndpoints
// OperatingSystem returns the operating system the provider is for.

View File

@@ -13,6 +13,8 @@ import (
"github.com/virtual-kubelet/virtual-kubelet/manager"
"github.com/virtual-kubelet/virtual-kubelet/providers/azure"
"github.com/virtual-kubelet/virtual-kubelet/providers/hypersh"
"github.com/virtual-kubelet/virtual-kubelet/providers/mock"
"github.com/virtual-kubelet/virtual-kubelet/providers/web"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -62,16 +64,18 @@ func New(nodeName, operatingSystem, namespace, kubeConfig, taint, provider, prov
rm := manager.NewResourceManager(clientset)
daemonEndpointPortEnv := os.Getenv("KUBELET_PORT")
if daemonEndpointPortEnv == "" {
daemonEndpointPortEnv = "10250"
}
i64value, err := strconv.ParseInt(daemonEndpointPortEnv, 10, 32)
daemonEndpointPort := int32(i64value)
internalIP := os.Getenv("VKUBELET_POD_IP")
var p Provider
switch provider {
case "azure":
internalIP := os.Getenv("VKUBELET_POD_IP")
daemonEndpointPortEnv := os.Getenv("KUBELET_PORT")
i64value, err := strconv.ParseInt(daemonEndpointPortEnv, 10, 32)
daemonEndpointPort := int32(i64value)
if err != nil {
return nil, err
}
p, err = azure.NewACIProvider(providerConfig, rm, nodeName, operatingSystem, internalIP, daemonEndpointPort)
if err != nil {
return nil, err
@@ -81,6 +85,16 @@ func New(nodeName, operatingSystem, namespace, kubeConfig, taint, provider, prov
if err != nil {
return nil, err
}
case "web":
p, err = web.NewBrokerProvider(nodeName, operatingSystem, daemonEndpointPort)
if err != nil {
return nil, err
}
case "mock":
p, err = mock.NewMockProvider(nodeName, operatingSystem, internalIP, daemonEndpointPort)
if err != nil {
return nil, err
}
default:
fmt.Printf("Provider '%s' is not supported\n", provider)
}
@@ -126,11 +140,9 @@ func (s *Server) registerNode() error {
ObjectMeta: metav1.ObjectMeta{
Name: s.nodeName,
Labels: map[string]string{
"type": "virtual-kubelet",
"kubernetes.io/role": "agent",
"beta.kubernetes.io/os": strings.ToLower(s.provider.OperatingSystem()),
},
Annotations: map[string]string{
"type": "virtual-kubelet",
"kubernetes.io/role": "agent",
"beta.kubernetes.io/os": strings.ToLower(s.provider.OperatingSystem()),
"alpha.service-controller.kubernetes.io/exclude-balancer": "true",
},
},
@@ -143,10 +155,10 @@ func (s *Server) registerNode() error {
Architecture: "amd64",
KubeletVersion: "v1.8.3",
},
Capacity: s.provider.Capacity(),
Allocatable: s.provider.Capacity(),
Conditions: s.provider.NodeConditions(),
Addresses: s.provider.NodeAddresses(),
Capacity: s.provider.Capacity(),
Allocatable: s.provider.Capacity(),
Conditions: s.provider.NodeConditions(),
Addresses: s.provider.NodeAddresses(),
DaemonEndpoints: *s.provider.NodeDaemonEndpoints(),
},
}