Compare commits
37 Commits
v0.2-beta
...
v0.2-beta-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d8b865848 | ||
|
|
3c87000b71 | ||
|
|
a99f2d08c1 | ||
|
|
f4ebbfc7a3 | ||
|
|
d23ac6679c | ||
|
|
cf30854bb1 | ||
|
|
6b6d71b5b9 | ||
|
|
077263af25 | ||
|
|
6486f65166 | ||
|
|
ac90d3ab13 | ||
|
|
dadd97de8d | ||
|
|
35acb384e1 | ||
|
|
5f9c46a80c | ||
|
|
a48f9866f2 | ||
|
|
c686fdffcb | ||
|
|
4554635194 | ||
|
|
45630ed369 | ||
|
|
4a8d43f736 | ||
|
|
3668b0d6b9 | ||
|
|
7d47eb1409 | ||
|
|
f78a8e9522 | ||
|
|
980cea6a07 | ||
|
|
8c0345edcf | ||
|
|
9eb0cf535f | ||
|
|
61d5440dc3 | ||
|
|
f9d1805ba0 | ||
|
|
909819c69d | ||
|
|
d27616776c | ||
|
|
2ca933fe27 | ||
|
|
b92c7320ca | ||
|
|
4fb11ecd87 | ||
|
|
a21cfff420 | ||
|
|
d690131565 | ||
|
|
2c47b6b573 | ||
|
|
922364ee5f | ||
|
|
87011f266e | ||
|
|
a4e99c2133 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -22,3 +22,6 @@ bin/
|
||||
|
||||
# Test credentials file
|
||||
credentials.json
|
||||
|
||||
# VS Code files
|
||||
.vscode/
|
||||
8
Gopkg.lock
generated
8
Gopkg.lock
generated
@@ -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
16
ISSUE_TEMPLATE.md
Normal 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
|
||||
51
README.md
51
README.md
@@ -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.
BIN
charts/virtual-kubelet-for-aks-0.1.3.tgz
Normal file
BIN
charts/virtual-kubelet-for-aks-0.1.3.tgz
Normal file
Binary file not shown.
8
charts/virtual-kubelet-for-aks/Chart.yaml
Normal file
8
charts/virtual-kubelet-for-aks/Chart.yaml
Normal 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
|
||||
5
charts/virtual-kubelet-for-aks/templates/NOTES.txt
Normal file
5
charts/virtual-kubelet-for-aks/templates/NOTES.txt
Normal 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" . }}"
|
||||
16
charts/virtual-kubelet-for-aks/templates/_helpers.tpl
Normal file
16
charts/virtual-kubelet-for-aks/templates/_helpers.tpl
Normal 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 -}}
|
||||
57
charts/virtual-kubelet-for-aks/templates/deployment.yaml
Normal file
57
charts/virtual-kubelet-for-aks/templates/deployment.yaml
Normal 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
|
||||
9
charts/virtual-kubelet-for-aks/templates/secrets.yaml
Normal file
9
charts/virtual-kubelet-for-aks/templates/secrets.yaml
Normal 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 }}
|
||||
16
charts/virtual-kubelet-for-aks/values.yaml
Normal file
16
charts/virtual-kubelet-for-aks/values.yaml
Normal 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:
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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
10
providers/README.md
Normal 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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
38
providers/azure/acsCredential.go
Normal file
38
providers/azure/acsCredential.go
Normal 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
|
||||
}
|
||||
129
providers/azure/acsCredential_test.go
Normal file
129
providers/azure/acsCredential_test.go
Normal 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")
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
Region = "westus"
|
||||
ResourceGroup = "virtual-kubeletrg"
|
||||
OperatingSystem = "Linux"
|
||||
CPU = 100
|
||||
Memory = 100Gi
|
||||
Pods = 50
|
||||
CPU = "100"
|
||||
Memory = "100Gi"
|
||||
Pods = "50"
|
||||
|
||||
@@ -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
260
providers/mock/mock.go
Normal 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
277
providers/web/broker.go
Normal 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
|
||||
}
|
||||
21
providers/web/charts/virtual-kubelet-web/.helmignore
Normal file
21
providers/web/charts/virtual-kubelet-web/.helmignore
Normal 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
|
||||
4
providers/web/charts/virtual-kubelet-web/Chart.yaml
Normal file
4
providers/web/charts/virtual-kubelet-web/Chart.yaml
Normal 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
|
||||
|
||||
@@ -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" . }}"
|
||||
@@ -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 -}}
|
||||
@@ -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 }}]
|
||||
11
providers/web/charts/virtual-kubelet-web/values.yaml
Normal file
11
providers/web/charts/virtual-kubelet-web/values.yaml
Normal 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
|
||||
4
providers/web/rust-web-provider/.gitignore
vendored
Normal file
4
providers/web/rust-web-provider/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/target/
|
||||
**/*.rs.bk
|
||||
.idea/
|
||||
.vscode/
|
||||
988
providers/web/rust-web-provider/Cargo.lock
generated
Normal file
988
providers/web/rust-web-provider/Cargo.lock
generated
Normal 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"
|
||||
11
providers/web/rust-web-provider/Cargo.toml
Normal file
11
providers/web/rust-web-provider/Cargo.toml
Normal 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"
|
||||
9
providers/web/rust-web-provider/Dockerfile
Normal file
9
providers/web/rust-web-provider/Dockerfile
Normal 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" ]
|
||||
75
providers/web/rust-web-provider/scripts/buildDocker.sh
Executable file
75
providers/web/rust-web-provider/scripts/buildDocker.sh
Executable 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
|
||||
20
providers/web/rust-web-provider/src/main.rs
Normal file
20
providers/web/rust-web-provider/src/main.rs
Normal 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();
|
||||
}
|
||||
156
providers/web/rust-web-provider/src/unit_provider.rs
Normal file
156
providers/web/rust-web-provider/src/unit_provider.rs
Normal 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")
|
||||
}
|
||||
}
|
||||
18
providers/web/rust-web-provider/src/utils.rs
Normal file
18
providers/web/rust-web-provider/src/utils.rs
Normal 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
22
vendor/github.com/cenkalti/backoff/.gitignore
generated
vendored
Normal 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
9
vendor/github.com/cenkalti/backoff/.travis.yml
generated
vendored
Normal 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
20
vendor/github.com/cenkalti/backoff/LICENSE
generated
vendored
Normal 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
30
vendor/github.com/cenkalti/backoff/README.md
generated
vendored
Normal 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
66
vendor/github.com/cenkalti/backoff/backoff.go
generated
vendored
Normal 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
27
vendor/github.com/cenkalti/backoff/backoff_test.go
generated
vendored
Normal 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
60
vendor/github.com/cenkalti/backoff/context.go
generated
vendored
Normal 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
26
vendor/github.com/cenkalti/backoff/context_test.go
generated
vendored
Normal 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
73
vendor/github.com/cenkalti/backoff/example_test.go
generated
vendored
Normal 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
156
vendor/github.com/cenkalti/backoff/exponential.go
generated
vendored
Normal 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
108
vendor/github.com/cenkalti/backoff/exponential_test.go
generated
vendored
Normal 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
78
vendor/github.com/cenkalti/backoff/retry.go
generated
vendored
Normal 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
99
vendor/github.com/cenkalti/backoff/retry_test.go
generated
vendored
Normal 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
81
vendor/github.com/cenkalti/backoff/ticker.go
generated
vendored
Normal 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
94
vendor/github.com/cenkalti/backoff/ticker_test.go
generated
vendored
Normal 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
35
vendor/github.com/cenkalti/backoff/tries.go
generated
vendored
Normal 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
55
vendor/github.com/cenkalti/backoff/tries_test.go
generated
vendored
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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(),
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user