diff --git a/.dockerignore b/.dockerignore
index 122da4923..81f91339f 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -37,20 +37,47 @@
!target/x86_64-unknown-linux-gnu/release/controller
!target/x86_64-unknown-linux-gnu/debug/agent
!target/x86_64-unknown-linux-gnu/release/agent
+!target/x86_64-unknown-linux-gnu/debug/agent-full
+!target/x86_64-unknown-linux-gnu/release/agent-full
!target/x86_64-unknown-linux-gnu/debug/udev-video-broker
!target/x86_64-unknown-linux-gnu/release/udev-video-broker
+!target/x86_64-unknown-linux-gnu/debug/webhook-configuration
+!target/x86_64-unknown-linux-gnu/release/webhook-configuration
+!target/x86_64-unknown-linux-gnu/release/udev-discovery-handler
+!target/x86_64-unknown-linux-gnu/release/debug-echo-discovery-handler
+!target/x86_64-unknown-linux-gnu/release/http-discovery-handler
+!target/x86_64-unknown-linux-gnu/release/onvif-discovery-handler
+!target/x86_64-unknown-linux-gnu/release/opcua-discovery-handler
!target/aarch64-unknown-linux-gnu/debug/controller
!target/aarch64-unknown-linux-gnu/release/controller
!target/aarch64-unknown-linux-gnu/debug/agent
!target/aarch64-unknown-linux-gnu/release/agent
+!target/aarch64-unknown-linux-gnu/debug/agent-full
+!target/aarch64-unknown-linux-gnu/release/agent-full
!target/aarch64-unknown-linux-gnu/debug/udev-video-broker
!target/aarch64-unknown-linux-gnu/release/udev-video-broker
+!target/aarch64-unknown-linux-gnu/debug/webhook-configuration
+!target/aarch64-unknown-linux-gnu/release/webhook-configuration
+!target/aarch64-unknown-linux-gnu/release/udev-discovery-handler
+!target/aarch64-unknown-linux-gnu/release/debug-echo-discovery-handler
+!target/aarch64-unknown-linux-gnu/release/http-discovery-handler
+!target/aarch64-unknown-linux-gnu/release/onvif-discovery-handler
+!target/aarch64-unknown-linux-gnu/release/opcua-discovery-handler
!target/armv7-unknown-linux-gnueabihf/debug/controller
!target/armv7-unknown-linux-gnueabihf/release/controller
!target/armv7-unknown-linux-gnueabihf/debug/agent
!target/armv7-unknown-linux-gnueabihf/release/agent
+!target/armv7-unknown-linux-gnueabihf/debug/agent-full
+!target/armv7-unknown-linux-gnueabihf/release/agent-full
!target/armv7-unknown-linux-gnueabihf/debug/udev-video-broker
!target/armv7-unknown-linux-gnueabihf/release/udev-video-broker
+!target/armv7-unknown-linux-gnueabihf/debug/webhook-configuration
+!target/armv7-unknown-linux-gnueabihf/release/webhook-configuration
+!target/armv7-unknown-linux-gnueabihf/release/udev-discovery-handler
+!target/armv7-unknown-linux-gnueabihf/release/debug-echo-discovery-handler
+!target/armv7-unknown-linux-gnueabihf/release/http-discovery-handler
+!target/armv7-unknown-linux-gnueabihf/release/onvif-discovery-handler
+!target/armv7-unknown-linux-gnueabihf/release/opcua-discovery-handler
# Cross toml file needs to be available for making the cross build containers
!Cross.toml
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 4ec3b06c1..635b3f725 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1,3 +1,3 @@
# https://help.github.com/en/articles/about-code-owners#codeowners-syntax
-* @bfjelds @kate-goldenring @jiria @britel
\ No newline at end of file
+* @bfjelds @kate-goldenring @jiria @britel @romoh
diff --git a/.github/actions/build-component-per-arch/main.js b/.github/actions/build-component-per-arch/main.js
index 665e6c9db..d3e4a7e5f 100644
--- a/.github/actions/build-component-per-arch/main.js
+++ b/.github/actions/build-component-per-arch/main.js
@@ -62,11 +62,12 @@ async function shell_cmd(cmd) {
core.setFailed(`Failed with unknown platform: ${core.getInput('platform')}`)
return
}
+
console.log(`Makefile build target suffix: ${makefile_target_suffix}`)
if (core.getInput('build_rust') == '1') {
console.log(`Install Rust`)
- child_process.execSync(`curl https://sh.rustup.rs | sh -s -- -y --default-toolchain=1.41.0`);
+ child_process.execSync(`curl https://sh.rustup.rs | sh -s -- -y --default-toolchain=1.51.0`);
const bindir = `${process.env.HOME}/.cargo/bin`;
process.env.PATH = `${process.env.PATH}:${bindir}`;
diff --git a/.github/workflows/auto-update-dependencies.yml b/.github/workflows/auto-update-dependencies.yml
new file mode 100644
index 000000000..766a8188a
--- /dev/null
+++ b/.github/workflows/auto-update-dependencies.yml
@@ -0,0 +1,33 @@
+name: Dependencies autoupdate
+
+on:
+ workflow_dispatch: # can be manually run if needed
+ schedule:
+ - cron: '0 0 1 * *' # run on first day of each month at 12:00 am UTC time
+
+env:
+ CARGO_TERM_COLOR: always
+
+jobs:
+ auto-update-dependencies:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout the head commit of the branch
+ uses: actions/checkout@v2
+ with:
+ persist-credentials: false
+
+ - name: Install Linux requirements
+ run: |
+ apt_dependencies="git curl libssl-dev pkg-config libudev-dev libv4l-dev"
+ echo "Run apt update and apt install the following dependencies: $apt_dependencies"
+ sudo apt update
+ sudo apt install -y $apt_dependencies
+
+ - name: Check for dependency updates
+ uses: romoh/dependencies-autoupdate@v1.1
+ with:
+ token: ${{ secrets.AKRI_BOT_TOKEN }}
+ update-command: "'cargo update && cargo test'"
+ on-changes-command: "'./version.sh -u -p'"
diff --git a/.github/workflows/build-agent-container.yml b/.github/workflows/build-agent-container.yml
index db45d0ce8..f092b7155 100644
--- a/.github/workflows/build-agent-container.yml
+++ b/.github/workflows/build-agent-container.yml
@@ -1,41 +1,16 @@
-name: Build Agent
+name: Build Agents
on:
- push:
- branches: [ main ]
- paths:
- - .github/actions/build-component-per-arch/**
- - .github/actions/build-component-multi-arch/**
- - .github/workflows/build-agent-container.yml
- - build/containers/Dockerfile.agent
- - agent/**
- - shared/**
- - version.txt
- - build/akri-containers.mk
- - Makefile
- pull_request:
- branches: [ main ]
- paths:
- - .github/actions/build-component-per-arch/**
- - .github/actions/build-component-multi-arch/**
- - .github/workflows/build-agent-container.yml
- - build/containers/Dockerfile.agent
- - agent/**
- - shared/**
- - version.txt
- - build/akri-containers.mk
- - Makefile
- release:
- types:
- - published
-
-env:
- AKRI_COMPONENT: agent
- MAKEFILE_COMPONENT: agent
+ workflow_run:
+ workflows: ["Build Production Rust Code"]
+ branches: [main]
+ types:
+ - completed
jobs:
per-arch:
+ if: ${{ github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
timeout-minutes: 40
strategy:
@@ -44,6 +19,9 @@ jobs:
- arm64v8
- arm32v7
- amd64
+ akri-component:
+ - agent
+ - agent-full
steps:
- name: Checkout the head commit of the branch
@@ -60,27 +38,43 @@ jobs:
yarn install
yarn add @actions/core @actions/github @actions/exec fs
- - name: Run Per-Arch component build for ${{ env.AKRI_COMPONENT }}
+ - name: Download rust build artifacts
+ uses: dawidd6/action-download-artifact@v2
+ with:
+ workflow: ${{ github.event.workflow_run.workflow_id }}
+ workflow_conclusion: success
+ commit: ${{ github.event.workflow_run.head_sha }}
+ name: rust-${{ matrix.arch }}-binaries
+ path: /tmp
+
+ - name: Unpack Rust binaries
+ run: |
+ tar -xvf /tmp/rust-${{ matrix.arch }}-binaries.tar
+
+ - name: Run Per-Arch component build for ${{ matrix.akri-component }}
uses: ./.github/actions/build-component-per-arch
with:
- github_event_name: ${{ github.event_name }}
+ github_event_name: ${{ github.event.workflow_run.event }}
github_ref: ${{ github.ref }}
- github_event_action: ${{ github.event.action }}
- github_merged: ${{ github.event.pull_request.merged }}
- container_name: ${{ env.AKRI_COMPONENT }}
+ container_name: ${{ matrix.akri-component }}
container_prefix: ghcr.io/deislabs/akri
container_registry_base_url: ghcr.io
container_registry_username: ${{ secrets.crUsername }}
container_registry_password: ${{ secrets.crPassword }}
- makefile_component_name: ${{ env.MAKEFILE_COMPONENT }}
+ makefile_component_name: ${{ matrix.akri-component }}
platform: ${{ matrix.arch }}
- build_rust: "1"
+ build_rust: "0"
multi-arch:
- if: (github.event_name == 'release') || (github.event_name == 'push' && github.ref == 'refs/heads/main') || (startsWith(github.event_name, 'pull_request') && github.event.action == 'closed' && github.event.pull_request.merged == true && github.ref != 'refs/heads/main')
+ if: ${{ github.event.workflow_run.conclusion == 'success' }} && ((github.event.workflow_run.event == 'release') || (github.event.workflow_run.event == 'push' && github.ref == 'refs/heads/main'))
needs: per-arch
runs-on: ubuntu-latest
timeout-minutes: 5
+ strategy:
+ matrix:
+ akri-component:
+ - agent
+ - agent-full
steps:
- name: Checkout repo
@@ -97,13 +91,13 @@ jobs:
yarn install
yarn add @actions/core @actions/github @actions/exec fs
- - name: Run Multi-Arch component build for ${{ env.AKRI_COMPONENT }}
+ - name: Run Multi-Arch component build for ${{ matrix.akri-component }}
uses: ./.github/actions/build-component-multi-arch
with:
- github_event_name: ${{ github.event_name }}
- container_name: ${{ env.AKRI_COMPONENT }}
+ github_event_name: ${{ github.event.workflow_run.event }}
+ container_name: ${{ matrix.akri-component }}
container_prefix: ghcr.io/deislabs/akri
container_registry_base_url: ghcr.io
container_registry_username: ${{ secrets.crUsername }}
container_registry_password: ${{ secrets.crPassword }}
- makefile_component_name: ${{ env.MAKEFILE_COMPONENT }}
+ makefile_component_name: ${{ matrix.akri-component }}
diff --git a/.github/workflows/build-anomaly-detection-app-container.yml b/.github/workflows/build-anomaly-detection-app-container.yml
index 3a0afc5d2..130a1fa53 100644
--- a/.github/workflows/build-anomaly-detection-app-container.yml
+++ b/.github/workflows/build-anomaly-detection-app-container.yml
@@ -11,6 +11,7 @@ on:
- samples/apps/anomaly-detection-app/**
- version.txt
- build/akri-containers.mk
+ - build/akri-python-containers.mk
- Makefile
pull_request:
branches: [ main ]
@@ -22,6 +23,7 @@ on:
- samples/apps/anomaly-detection-app/**
- version.txt
- build/akri-containers.mk
+ - build/akri-python-containers.mk
- Makefile
release:
types:
diff --git a/.github/workflows/build-controller-container.yml b/.github/workflows/build-controller-container.yml
index 1330df84c..8c512f252 100644
--- a/.github/workflows/build-controller-container.yml
+++ b/.github/workflows/build-controller-container.yml
@@ -1,33 +1,11 @@
name: Build Controller
on:
- push:
- branches: [ main ]
- paths:
- - .github/actions/build-component-per-arch/**
- - .github/actions/build-component-multi-arch/**
- - .github/workflows/build-controller-container.yml
- - build/containers/Dockerfile.controller
- - controller/**
- - shared/**
- - version.txt
- - build/akri-containers.mk
- - Makefile
- pull_request:
- branches: [ main ]
- paths:
- - .github/actions/build-component-per-arch/**
- - .github/actions/build-component-multi-arch/**
- - .github/workflows/build-controller-container.yml
- - build/containers/Dockerfile.controller
- - controller/**
- - shared/**
- - version.txt
- - build/akri-containers.mk
- - Makefile
- release:
- types:
- - published
+ workflow_run:
+ workflows: ["Build Production Rust Code"]
+ branches: [main]
+ types:
+ - completed
env:
AKRI_COMPONENT: controller
@@ -36,6 +14,7 @@ env:
jobs:
per-arch:
+ if: ${{ github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
timeout-minutes: 40
strategy:
@@ -59,13 +38,24 @@ jobs:
yarn install
yarn add @actions/core @actions/github @actions/exec fs
+ - name: Download rust build artifacts
+ uses: dawidd6/action-download-artifact@v2
+ with:
+ workflow: ${{ github.event.workflow_run.workflow_id }}
+ workflow_conclusion: success
+ commit: ${{ github.event.workflow_run.head_sha }}
+ name: rust-${{ matrix.arch }}-binaries
+ path: /tmp
+
+ - name: Unpack Rust binaries
+ run: |
+ tar -xvf /tmp/rust-${{ matrix.arch }}-binaries.tar
+
- name: Run Per-Arch component build for ${{ env.AKRI_COMPONENT }}
uses: ./.github/actions/build-component-per-arch
with:
- github_event_name: ${{ github.event_name }}
+ github_event_name: ${{ github.event.workflow_run.event }}
github_ref: ${{ github.ref }}
- github_event_action: ${{ github.event.action }}
- github_merged: ${{ github.event.pull_request.merged }}
container_name: ${{ env.AKRI_COMPONENT }}
container_prefix: ghcr.io/deislabs/akri
container_registry_base_url: ghcr.io
@@ -73,10 +63,10 @@ jobs:
container_registry_password: ${{ secrets.crPassword }}
makefile_component_name: ${{ env.MAKEFILE_COMPONENT }}
platform: ${{ matrix.arch }}
- build_rust: "1"
+ build_rust: "0"
multi-arch:
- if: (github.event_name == 'release') || (github.event_name == 'push' && github.ref == 'refs/heads/main') || (startsWith(github.event_name, 'pull_request') && github.event.action == 'closed' && github.event.pull_request.merged == true && github.ref != 'refs/heads/main')
+ if: ${{ github.event.workflow_run.conclusion == 'success' }} && ((github.event.workflow_run.event == 'release') || (github.event.workflow_run.event == 'push' && github.ref == 'refs/heads/main'))
needs: per-arch
runs-on: ubuntu-latest
timeout-minutes: 5
@@ -99,7 +89,7 @@ jobs:
- name: Run Multi-Arch component build for ${{ env.AKRI_COMPONENT }}
uses: ./.github/actions/build-component-multi-arch
with:
- github_event_name: ${{ github.event_name }}
+ github_event_name: ${{ github.event.workflow_run.event }}
container_name: ${{ env.AKRI_COMPONENT }}
container_prefix: ghcr.io/deislabs/akri
container_registry_base_url: ghcr.io
diff --git a/.github/workflows/build-discovery-handlers.yml b/.github/workflows/build-discovery-handlers.yml
new file mode 100644
index 000000000..780522f51
--- /dev/null
+++ b/.github/workflows/build-discovery-handlers.yml
@@ -0,0 +1,107 @@
+name: Build Discovery Handlers
+
+on:
+ workflow_run:
+ workflows: ["Build Production Rust Code"]
+ branches: [main]
+ types:
+ - completed
+
+jobs:
+
+ per-arch:
+ if: ${{ github.event.workflow_run.conclusion == 'success' }}
+ runs-on: ubuntu-latest
+ timeout-minutes: 40
+ strategy:
+ matrix:
+ arch:
+ - arm64v8
+ - arm32v7
+ - amd64
+ akri-component:
+ - debug-echo-discovery
+ - udev-discovery
+ - onvif-discovery
+ - opcua-discovery
+
+ steps:
+ - name: Checkout the head commit of the branch
+ uses: actions/checkout@v2
+ with:
+ persist-credentials: false
+
+ - name: Prepare To Install
+ uses: actions/setup-node@v1
+ with:
+ node-version: 12
+ - name: Install Deps
+ run: |
+ yarn install
+ yarn add @actions/core @actions/github @actions/exec fs
+
+ - name: Download rust build artifacts
+ uses: dawidd6/action-download-artifact@v2
+ with:
+ workflow: ${{ github.event.workflow_run.workflow_id }}
+ workflow_conclusion: success
+ commit: ${{ github.event.workflow_run.head_sha }}
+ name: rust-${{ matrix.arch }}-binaries
+ path: /tmp
+
+ - name: Unpack Rust binaries
+ run: |
+ tar -xvf /tmp/rust-${{ matrix.arch }}-binaries.tar
+
+ - name: Run Per-Arch component build for ${{ matrix.akri-component }}
+ uses: ./.github/actions/build-component-per-arch
+ with:
+ github_event_name: ${{ github.event.workflow_run.event }}
+ github_ref: ${{ github.ref }}
+ container_name: ${{ matrix.akri-component }}
+ container_prefix: ghcr.io/deislabs/akri
+ container_registry_base_url: ghcr.io
+ container_registry_username: ${{ secrets.crUsername }}
+ container_registry_password: ${{ secrets.crPassword }}
+ makefile_component_name: ${{ matrix.akri-component }}
+ platform: ${{ matrix.arch }}
+ build_rust: "0"
+
+ multi-arch:
+ if: ${{ github.event.workflow_run.conclusion == 'success' }} && ((github.event.workflow_run.event == 'release') || (github.event.workflow_run.event == 'push' && github.ref == 'refs/heads/main'))
+ needs: per-arch
+ runs-on: ubuntu-latest
+ timeout-minutes: 5
+ strategy:
+ matrix:
+ akri-component:
+ - debug-echo-discovery
+ - udev-discovery
+ - onvif-discovery
+ - opcua-discovery
+
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v2
+ with:
+ persist-credentials: false
+
+ - name: Prepare To Install
+ uses: actions/setup-node@v1
+ with:
+ node-version: 12
+ - name: Install Deps
+ run: |
+ yarn install
+ yarn add @actions/core @actions/github @actions/exec fs
+
+ - name: Run Multi-Arch component build for ${{ matrix.akri-component }}
+ uses: ./.github/actions/build-component-multi-arch
+ with:
+ github_event_name: ${{ github.event.workflow_run.event }}
+ container_name: ${{ matrix.akri-component }}
+ container_prefix: ghcr.io/deislabs/akri
+ container_registry_base_url: ghcr.io
+ container_registry_username: ${{ secrets.crUsername }}
+ container_registry_password: ${{ secrets.crPassword }}
+ makefile_component_name: ${{ matrix.akri-component }}
diff --git a/.github/workflows/build-onvif-video-broker-container.yml b/.github/workflows/build-onvif-video-broker-container.yml
index 0f69d1afd..e0653a50d 100644
--- a/.github/workflows/build-onvif-video-broker-container.yml
+++ b/.github/workflows/build-onvif-video-broker-container.yml
@@ -11,6 +11,7 @@ on:
- samples/brokers/onvif-video-broker/**
- version.txt
- build/akri-containers.mk
+ - build/akri-dotnet-containers.mk
- Makefile
pull_request:
branches: [ main ]
@@ -22,6 +23,7 @@ on:
- samples/brokers/onvif-video-broker/**
- version.txt
- build/akri-containers.mk
+ - build/akri-dotnet-containers.mk
- Makefile
release:
types:
diff --git a/.github/workflows/build-opcua-monitoring-broker-container.yml b/.github/workflows/build-opcua-monitoring-broker-container.yml
index ce2edbebf..690888bf8 100644
--- a/.github/workflows/build-opcua-monitoring-broker-container.yml
+++ b/.github/workflows/build-opcua-monitoring-broker-container.yml
@@ -11,6 +11,7 @@ on:
- samples/brokers/opcua-monitoring-broker/**
- version.txt
- build/akri-containers.mk
+ - build/akri-dotnet-containers.mk
- Makefile
pull_request:
branches: [ main ]
@@ -22,6 +23,7 @@ on:
- samples/brokers/opcua-monitoring-broker/**
- version.txt
- build/akri-containers.mk
+ - build/akri-dotnet-containers.mk
- Makefile
release:
types:
@@ -72,7 +74,7 @@ jobs:
container_registry_password: ${{ secrets.crPassword }}
makefile_component_name: ${{ env.MAKEFILE_COMPONENT }}
platform: ${{ matrix.arch }}
- build_rust: "1"
+ build_rust: "0"
multi-arch:
if: (github.event_name == 'release') || (github.event_name == 'push' && github.ref == 'refs/heads/main') || (startsWith(github.event_name, 'pull_request') && github.event.action == 'closed' && github.event.pull_request.merged == true && github.ref != 'refs/heads/main')
diff --git a/.github/workflows/build-rust-code.yml b/.github/workflows/build-rust-code.yml
new file mode 100644
index 000000000..5fd394948
--- /dev/null
+++ b/.github/workflows/build-rust-code.yml
@@ -0,0 +1,103 @@
+name: Build Production Rust Code
+
+on:
+ push:
+ branches: [ main ]
+ paths:
+ - .github/actions/build-component-per-arch/**
+ - .github/actions/build-component-multi-arch/**
+ - .github/workflows/build-rust-code.yml
+ - .github/workflows/build-agent-container.yml
+ - .github/workflows/build-controller-container.yml
+ - .github/workflows/build-udev-video-broker-container.yml
+ - .github/workflows/build-webhook-configuration-container.yml
+ - build/containers/Dockerfile.agent
+ - build/containers/Dockerfile.controller
+ - build/containers/Dockerfile.udev-video-broker
+ - build/containers/Dockerfile.webhook-configuration
+ - '**.rs'
+ - '**/Cargo.toml'
+ - '**/Cargo.lock'
+ - version.txt
+ - build/akri-containers.mk
+ - build/akri-rust-containers.mk
+ - Makefile
+ pull_request:
+ branches: [ main ]
+ paths:
+ - .github/actions/build-component-per-arch/**
+ - .github/actions/build-component-multi-arch/**
+ - .github/workflows/build-rust-code.yml
+ - .github/workflows/build-agent-container.yml
+ - .github/workflows/build-controller-container.yml
+ - .github/workflows/build-udev-video-broker-container.yml
+ - .github/workflows/build-webhook-configuration-container.yml
+ - build/containers/Dockerfile.agent
+ - build/containers/Dockerfile.controller
+ - build/containers/Dockerfile.udev-video-broker
+ - build/containers/Dockerfile.webhook-configuration
+ - '**.rs'
+ - '**/Cargo.toml'
+ - '**/Cargo.lock'
+ - version.txt
+ - build/akri-containers.mk
+ - build/akri-rust-containers.mk
+ - Makefile
+ release:
+ types:
+ - published
+
+jobs:
+
+ build-rust:
+ runs-on: ubuntu-latest
+ timeout-minutes: 50
+ strategy:
+ matrix:
+ arch:
+ - label: arm64v8
+ make-target: arm64
+ rust-target-path: aarch64-unknown-linux-gnu
+ - label: arm32v7
+ make-target: arm32
+ rust-target-path: armv7-unknown-linux-gnueabihf
+ - label: amd64
+ make-target: amd64
+ rust-target-path: x86_64-unknown-linux-gnu
+
+ steps:
+ - name: Checkout the head commit of the branch
+ uses: actions/checkout@v2
+ with:
+ persist-credentials: false
+
+ - name: Rust install
+ uses: actions-rs/toolchain@v1
+ with:
+ toolchain: 1.51.0
+ override: true
+ components: clippy, rustfmt
+
+ - name: Build production rust for ${{ matrix.arch.label }}
+ env:
+ AGENT_FEATURES: "agent-full onvif-feat opcua-feat udev-feat"
+ run: |
+ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
+ make install-cross
+ cross --version
+ make akri-cross-build-${{ matrix.arch.make-target }}
+
+ - name: Package build binaries
+ run: |
+ tar_manifest='/tmp/tar-contents.txt'
+ > $tar_manifest
+ for f in target/${{ matrix.arch.rust-target-path }}/release/*; do filetype=$( file "$f" ); case "$filetype" in *ELF*) echo "$f" >> $tar_manifest ;; esac; done
+ tar -cvf /tmp/rust-${{ matrix.arch.label }}-binaries.tar `cat $tar_manifest`
+
+ - name: Upload target binaries as artifact
+ uses: actions/upload-artifact@v2
+ with:
+ name: rust-${{ matrix.arch.label }}-binaries
+ path: /tmp/rust-${{ matrix.arch.label }}-binaries.tar
+ retention-days: 1
+
diff --git a/.github/workflows/build-udev-video-broker-container.yml b/.github/workflows/build-udev-video-broker-container.yml
index e2e29a932..7d610124f 100644
--- a/.github/workflows/build-udev-video-broker-container.yml
+++ b/.github/workflows/build-udev-video-broker-container.yml
@@ -1,33 +1,11 @@
name: Build UDEV Broker
on:
- push:
- branches: [ main ]
- paths:
- - .github/actions/build-component-per-arch/**
- - .github/actions/build-component-multi-arch/**
- - .github/workflows/build-udev-video-broker-container.yml
- - build/containers/Dockerfile.udev-video-broker
- - samples/brokers/udev-video-broker/**
- - shared/**
- - version.txt
- - build/akri-containers.mk
- - Makefile
- pull_request:
- branches: [ main ]
- paths:
- - .github/actions/build-component-per-arch/**
- - .github/actions/build-component-multi-arch/**
- - .github/workflows/build-udev-video-broker-container.yml
- - build/containers/Dockerfile.udev-video-broker
- - samples/brokers/udev-video-broker/**
- - shared/**
- - version.txt
- - build/akri-containers.mk
- - Makefile
- release:
- types:
- - published
+ workflow_run:
+ workflows: ["Build Production Rust Code"]
+ branches: [main]
+ types:
+ - completed
env:
AKRI_COMPONENT: udev-video-broker
@@ -36,6 +14,7 @@ env:
jobs:
per-arch:
+ if: ${{ github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
timeout-minutes: 40
strategy:
@@ -60,13 +39,24 @@ jobs:
yarn install
yarn add @actions/core @actions/github @actions/exec fs
+ - name: Download rust build artifacts
+ uses: dawidd6/action-download-artifact@v2
+ with:
+ workflow: ${{ github.event.workflow_run.workflow_id }}
+ workflow_conclusion: success
+ commit: ${{ github.event.workflow_run.head_sha }}
+ name: rust-${{ matrix.arch }}-binaries
+ path: /tmp
+
+ - name: Unpack Rust binaries
+ run: |
+ tar -xvf /tmp/rust-${{ matrix.arch }}-binaries.tar
+
- name: Run Per-Arch component build for ${{ env.AKRI_COMPONENT }}
uses: ./.github/actions/build-component-per-arch
with:
- github_event_name: ${{ github.event_name }}
+ github_event_name: ${{ github.event.workflow_run.event }}
github_ref: ${{ github.ref }}
- github_event_action: ${{ github.event.action }}
- github_merged: ${{ github.event.pull_request.merged }}
container_name: ${{ env.AKRI_COMPONENT }}
container_prefix: ghcr.io/deislabs/akri
container_registry_base_url: ghcr.io
@@ -74,10 +64,10 @@ jobs:
container_registry_password: ${{ secrets.crPassword }}
makefile_component_name: ${{ env.MAKEFILE_COMPONENT }}
platform: ${{ matrix.arch }}
- build_rust: "1"
+ build_rust: "0"
multi-arch:
- if: (github.event_name == 'release') || (github.event_name == 'push' && github.ref == 'refs/heads/main') || (startsWith(github.event_name, 'pull_request') && github.event.action == 'closed' && github.event.pull_request.merged == true && github.ref != 'refs/heads/main')
+ if: ${{ github.event.workflow_run.conclusion == 'success' }} && ((github.event.workflow_run.event == 'release') || (github.event.workflow_run.event == 'push' && github.ref == 'refs/heads/main'))
needs: per-arch
runs-on: ubuntu-latest
timeout-minutes: 5
@@ -100,7 +90,7 @@ jobs:
- name: Run Multi-Arch component build for ${{ env.AKRI_COMPONENT }}
uses: ./.github/actions/build-component-multi-arch
with:
- github_event_name: ${{ github.event_name }}
+ github_event_name: ${{ github.event.workflow_run.event }}
container_name: ${{ env.AKRI_COMPONENT }}
container_prefix: ghcr.io/deislabs/akri
container_registry_base_url: ghcr.io
diff --git a/.github/workflows/build-video-streaming-app-container.yml b/.github/workflows/build-video-streaming-app-container.yml
index a0d44872b..86c01c129 100644
--- a/.github/workflows/build-video-streaming-app-container.yml
+++ b/.github/workflows/build-video-streaming-app-container.yml
@@ -11,6 +11,7 @@ on:
- samples/apps/video-streaming-app/**
- version.txt
- build/akri-containers.mk
+ - build/akri-python-containers.mk
- Makefile
pull_request:
branches: [ main ]
@@ -22,6 +23,7 @@ on:
- samples/apps/video-streaming-app/**
- version.txt
- build/akri-containers.mk
+ - build/akri-python-containers.mk
- Makefile
release:
types:
diff --git a/.github/workflows/build-webhook-configuration-container.yml b/.github/workflows/build-webhook-configuration-container.yml
new file mode 100644
index 000000000..9704a8908
--- /dev/null
+++ b/.github/workflows/build-webhook-configuration-container.yml
@@ -0,0 +1,96 @@
+name: Build Webhook Configuration
+
+on:
+ workflow_run:
+ workflows: ["Build Production Rust Code"]
+ branches: [main]
+ types:
+ - completed
+
+env:
+ AKRI_COMPONENT: webhook-configuration
+ MAKEFILE_COMPONENT: webhook-configuration
+
+jobs:
+ per-arch:
+ if: ${{ github.event.workflow_run.conclusion == 'success' }}
+ runs-on: ubuntu-latest
+ timeout-minutes: 60
+ strategy:
+ matrix:
+ arch:
+ - arm64v8
+ - arm32v7
+ - amd64
+
+ steps:
+ - name: Checkout the head commit of the branch
+ uses: actions/checkout@v2
+ with:
+ persist-credentials: false
+
+ - name: Prepare To Install
+ uses: actions/setup-node@v1
+ with:
+ node-version: 12
+ - name: Install Deps
+ run: |
+ yarn install
+ yarn add @actions/core @actions/github @actions/exec fs
+
+ - name: Download rust build artifacts
+ uses: dawidd6/action-download-artifact@v2
+ with:
+ workflow: ${{ github.event.workflow_run.workflow_id }}
+ workflow_conclusion: success
+ commit: ${{ github.event.workflow_run.head_sha }}
+ name: rust-${{ matrix.arch }}-binaries
+ path: /tmp
+
+ - name: Unpack Rust binaries
+ run: |
+ tar -xvf /tmp/rust-${{ matrix.arch }}-binaries.tar
+
+ - name: Run Per-Arch component build for ${{ env.AKRI_COMPONENT }}
+ uses: ./.github/actions/build-component-per-arch
+ with:
+ github_event_name: ${{ github.event.workflow_run.event }}
+ github_ref: ${{ github.ref }}
+ container_name: ${{ env.AKRI_COMPONENT }}
+ container_prefix: ghcr.io/deislabs/akri
+ container_registry_base_url: ghcr.io
+ container_registry_username: ${{ secrets.crUsername }}
+ container_registry_password: ${{ secrets.crPassword }}
+ makefile_component_name: ${{ env.MAKEFILE_COMPONENT }}
+ platform: ${{ matrix.arch }}
+ build_rust: "0"
+
+ multi-arch:
+ if: ${{ github.event.workflow_run.conclusion == 'success' }} && ((github.event.workflow_run.event == 'release') || (github.event.workflow_run.event == 'push' && github.ref == 'refs/heads/main'))
+ needs: per-arch
+ runs-on: ubuntu-latest
+ timeout-minutes: 60
+
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v2
+
+ - name: Prepare To Install
+ uses: actions/setup-node@v1
+ with:
+ node-version: 12
+ - name: Install Deps
+ run: |
+ yarn install
+ yarn add @actions/core @actions/github @actions/exec fs
+
+ - name: Run Multi-Arch component build for ${{ env.AKRI_COMPONENT }}
+ uses: ./.github/actions/build-component-multi-arch
+ with:
+ github_event_name: ${{ github.event.workflow_run.event }}
+ container_name: ${{ env.AKRI_COMPONENT }}
+ container_prefix: ghcr.io/deislabs/akri
+ container_registry_base_url: ghcr.io
+ container_registry_username: ${{ secrets.crUsername }}
+ container_registry_password: ${{ secrets.crPassword }}
+ makefile_component_name: ${{ env.MAKEFILE_COMPONENT }}
diff --git a/.github/workflows/cancel-previous-pr-workflows.yml b/.github/workflows/cancel-previous-pr-workflows.yml
new file mode 100644
index 000000000..e5e5bc1d3
--- /dev/null
+++ b/.github/workflows/cancel-previous-pr-workflows.yml
@@ -0,0 +1,14 @@
+name: Cancel Previous PR Workflows
+on:
+ workflow_run:
+ workflows: ["Dependencies autoupdate", "Build Agents", "Build Anomaly Detection App Container", "Build Controller", "Build ONVIF Broker (.NET)", "Build OPC UA Monitoring Broker", "Build OpenCV Base", "Build Production Rust Code", "Build Rust CrossBuild", "Build UDEV Broker", "Build Video Streaming App Container", "Build Webhook Configuration", "Check Rust", "Check versioning", "Helm", "Tarpaulin Code Coverage", "Test K3s, Kubernetes, and MicroK8s"]
+ types:
+ - requested
+jobs:
+ cancel:
+ runs-on: ubuntu-latest
+ if: github.event.workflow_run.event == 'pull_request'
+ steps:
+ - uses: styfle/cancel-workflow-action@0.8.0
+ with:
+ workflow_id: ${{ github.event.workflow.id }}
\ No newline at end of file
diff --git a/.github/workflows/check-rust.yml b/.github/workflows/check-rust.yml
index 8b0a026de..8c06b7b26 100644
--- a/.github/workflows/check-rust.yml
+++ b/.github/workflows/check-rust.yml
@@ -22,7 +22,7 @@ env:
jobs:
build:
runs-on: ubuntu-latest
- timeout-minutes: 20
+ timeout-minutes: 30
steps:
- name: Checkout the head commit of the branch
@@ -33,7 +33,7 @@ jobs:
- name: Rust install
uses: actions-rs/toolchain@v1
with:
- toolchain: 1.41.0
+ toolchain: 1.51.0
override: true
components: clippy, rustfmt
- name: Install Linux requirements
diff --git a/.github/workflows/run-helm.yml b/.github/workflows/run-helm.yml
index ffb76df8a..15b56f1d8 100644
--- a/.github/workflows/run-helm.yml
+++ b/.github/workflows/run-helm.yml
@@ -34,7 +34,7 @@ jobs:
- uses: azure/setup-helm@v1
- name: Lint helm chart
- run: helm lint deployment/helm && echo "lint finished successfully" || echo ::warning::"lint found issues"
+ run: helm lint deployment/helm --values test/helm-lint-values.yaml && echo "lint finished successfully" || echo ::warning::"lint found issues"
helm:
@@ -68,7 +68,7 @@ jobs:
sed -i s/"useDevelopmentContainers: true"/"useDevelopmentContainers: true"/g ./deployment/helm/values.yaml
- name: Lint helm chart
- run: helm lint deployment/helm
+ run: helm lint deployment/helm --values test/helm-lint-values.yaml && echo "lint finished successfully" || echo ::warning::"lint found issues"
- name: Cache version.txt
run: |
diff --git a/.github/workflows/run-tarpaulin.yml b/.github/workflows/run-tarpaulin.yml
index ab346a363..1a5e4bdb8 100644
--- a/.github/workflows/run-tarpaulin.yml
+++ b/.github/workflows/run-tarpaulin.yml
@@ -20,7 +20,14 @@ env:
jobs:
build:
runs-on: ubuntu-latest
- timeout-minutes: 20
+ # There is a second, hidden timeout in this workflow. When the tarpaulin container is created,
+ # it is created with a CMD that sleeps for 600 minutes. A more reasonable value could be selected,
+ # but it seems easier to make it SOOOO big that timeout-minutes is likely to never be impacted by
+ # it.
+ #
+ # But, if this workflow is mysteriously timing out after 600 minutes, make changes to the docker
+ # create command in the Create tarpaulin instance step.
+ timeout-minutes: 30
steps:
- name: Checkout the head commit of the branch
@@ -29,7 +36,7 @@ jobs:
persist-credentials: false
- name: Create tarpaulin instance
- run: docker create --network host --security-opt seccomp=unconfined -v "${PWD}:/volume" xd009642/tarpaulin:0.12.2 bash -c "echo 'sleep 20m; echo bye' > /tmp/keep_alive.sh; chmod 777 /tmp/keep_alive.sh; /tmp/keep_alive.sh" > container_id.txt
+ run: docker create --network host --security-opt seccomp=unconfined -v "${PWD}:/volume" xd009642/tarpaulin:0.16.0 bash -c "echo 'sleep 600m; echo bye' > /tmp/keep_alive.sh; chmod 777 /tmp/keep_alive.sh; /tmp/keep_alive.sh" > container_id.txt
- name: Start tarpaulin instance
run: docker start $(cat container_id.txt)
- name: Install linux requirement in tarpaulin instance
diff --git a/.github/workflows/run-test-cases.yml b/.github/workflows/run-test-cases.yml
index 5146cf700..4d999bc7e 100644
--- a/.github/workflows/run-test-cases.yml
+++ b/.github/workflows/run-test-cases.yml
@@ -4,286 +4,349 @@ on:
workflow_dispatch:
inputs:
pull_request:
- branches: [ main ]
+ branches: [main]
paths:
- - test/run-end-to-end.py
- - test/run-conservation-of-broker-pod.py
- - test/run-helm-install-delete.py
- - test/shared_test_code.py
- - .github/workflows/run-test-cases.yml
- - build/containers/Dockerfile.agent
- - build/containers/Dockerfile.controller
- - deployment/helm/**
- - agent/**
- - controller/**
- - shared/**
- - version.txt
- - build/akri-containers.mk
- - Makefile
+ - test/run-end-to-end.py
+ - test/run-conservation-of-broker-pod.py
+ - test/run-helm-install-delete.py
+ - test/run-webhook.py
+ - test/shared_test_code.py
+ - .github/workflows/run-test-cases.yml
+ - build/containers/Dockerfile.agent
+ - build/containers/Dockerfile.controller
+ - deployment/helm/**
+ - agent/**
+ - controller/**
+ - shared/**
+ - version.txt
+ - build/akri-containers.mk
+ - Makefile
push:
- branches: [ main ]
+ branches: [main]
paths:
- - test/run-end-to-end.py
- - test/run-conservation-of-broker-pod.py
- - test/run-helm-install-delete.py
- - test/shared_test_code.py
- - .github/workflows/run-test-cases.yml
- - build/containers/Dockerfile.agent
- - build/containers/Dockerfile.controller
- - deployment/helm/**
- - agent/**
- - controller/**
- - shared/**
- - version.txt
- - build/akri-containers.mk
- - Makefile
+ - test/run-end-to-end.py
+ - test/run-conservation-of-broker-pod.py
+ - test/run-helm-install-delete.py
+ - test/run-webhook.py
+ - test/shared_test_code.py
+ - .github/workflows/run-test-cases.yml
+ - build/containers/Dockerfile.agent
+ - build/containers/Dockerfile.controller
+ - deployment/helm/**
+ - agent/**
+ - controller/**
+ - shared/**
+ - version.txt
+ - build/akri-containers.mk
+ - Makefile
release:
types:
- published
-
+
jobs:
build-containers:
runs-on: ubuntu-18.04
- timeout-minutes: 35
+ timeout-minutes: 60
steps:
- - name: Checkout the head commit of the branch
- uses: actions/checkout@v2
- with:
- persist-credentials: false
+ - name: Checkout the head commit of the branch
+ uses: actions/checkout@v2
+ with:
+ persist-credentials: false
- - name: Build local containers for PR tests
- if: startsWith(github.event_name, 'pull_request')
- env:
- BUILD_AMD64: 1
- BUILD_ARM32: 0
- BUILD_ARM64: 0
- PREFIX: ghcr.io/deislabs/akri
- LABEL_PREFIX: pr
- run: |
- make akri-build
- make controller-build-amd64
- make agent-build-amd64
- docker save ${PREFIX}/agent:${LABEL_PREFIX}-amd64 > agent.tar
- docker save ${PREFIX}/controller:${LABEL_PREFIX}-amd64 > controller.tar
+ - name: Build local containers for PR tests
+ if: startsWith(github.event_name, 'pull_request')
+ env:
+ BUILD_AMD64: 1
+ BUILD_ARM32: 0
+ BUILD_ARM64: 0
+ BUILD_SLIM_AGENT: 0
+ AGENT_FEATURES: "agent-full"
+ PACKAGES_TO_EXCLUDE: "akri-udev akri-onvif akri-opcua udev-video-broker debug-echo-discovery-handler onvif-discovery-handler opcua-discovery-handler udev-discovery-handler"
+ PREFIX: ghcr.io/deislabs/akri
+ LABEL_PREFIX: pr
+ CARGO_INCREMENTAL: 0
+ run: |
+ make akri-build
+ make controller-build-amd64
+ make agent-full-build-amd64
+ make webhook-configuration-build-amd64
+ docker save ${PREFIX}/agent-full:${LABEL_PREFIX}-amd64 > agent.tar
+ docker save ${PREFIX}/controller:${LABEL_PREFIX}-amd64 > controller.tar
+ docker save ${PREFIX}/webhook-configuration:${LABEL_PREFIX}-amd64 > webhook-configuration.tar
- - name: Upload Agent container as artifact
- if: startsWith(github.event_name, 'pull_request')
- uses: actions/upload-artifact@v2
- with:
- name: agent.tar
- path: agent.tar
- - name: Upload Controller container as artifact
- if: startsWith(github.event_name, 'pull_request')
- uses: actions/upload-artifact@v2
- with:
- name: controller.tar
- path: controller.tar
+ - name: Upload Agent container as artifact
+ if: startsWith(github.event_name, 'pull_request')
+ uses: actions/upload-artifact@v2
+ with:
+ name: agent.tar
+ path: agent.tar
+ - name: Upload Controller container as artifact
+ if: startsWith(github.event_name, 'pull_request')
+ uses: actions/upload-artifact@v2
+ with:
+ name: controller.tar
+ path: controller.tar
+ - name: Upload Webhook-Configuration container as artifact
+ if: startsWith(github.event_name, 'pull_request')
+ uses: actions/upload-artifact@v2
+ with:
+ name: webhook-configuration.tar
+ path: webhook-configuration.tar
test-cases:
needs: build-containers
runs-on: ubuntu-18.04
- timeout-minutes: 35
+ timeout-minutes: 60
strategy:
fail-fast: false
matrix:
- kube-runtime:
- - K3s-1.18
- - K3s-1.19
- - MicroK8s-1.18
- - MicroK8s-1.19
- - Kubernetes-1.16
- - Kubernetes-1.17
- - Kubernetes-1.18
- - Kubernetes-1.19
- test-case:
- - end-to-end
- include:
- - kube-runtime: MicroK8s-1.18
- kube-version: 1.18/stable
- - kube-runtime: MicroK8s-1.19
- kube-version: 1.19/stable
- - kube-runtime: K3s-1.18
- kube-version: v1.18.9+k3s1
- - kube-runtime: K3s-1.19
- kube-version: v1.19.4+k3s1
- - kube-runtime: Kubernetes-1.16
- kube-version: 1.16.15-00
- - kube-runtime: Kubernetes-1.17
- kube-version: 1.17.14-00
- - kube-runtime: Kubernetes-1.18
- kube-version: 1.18.12-00
- - kube-runtime: Kubernetes-1.19
- kube-version: 1.19.4-00
- - test-case: end-to-end
- test-file: test/run-end-to-end.py
+ kube:
+ - runtime: MicroK8s-1.16
+ version: 1.16/stable
+ crictl: v1.16.0
+ - runtime: MicroK8s-1.17
+ version: 1.17/stable
+ crictl: v1.16.0
+ - runtime: MicroK8s-1.18
+ version: 1.18/stable
+ crictl: v1.17.0
+ - runtime: MicroK8s-1.19
+ version: 1.19/stable
+ crictl: v1.17.0
+ - runtime: MicroK8s-1.20
+ version: 1.20/stable
+ crictl: v1.17.0
+ - runtime: MicroK8s-1.21
+ version: 1.21/stable
+ crictl: v1.17.0
+ - runtime: K3s-1.16
+ version: v1.16.14+k3s1
+ crictl: v1.16.0
+ - runtime: K3s-1.17
+ version: v1.17.17+k3s1
+ crictl: v1.16.0
+ - runtime: K3s-1.18
+ version: v1.18.9+k3s1
+ crictl: v1.17.0
+ - runtime: K3s-1.19
+ version: v1.19.10+k3s1
+ crictl: v1.17.0
+ - runtime: K3s-1.20
+ version: v1.20.6+k3s1
+ crictl: v1.17.0
+ - runtime: Kubernetes-1.16
+ version: 1.16.15-00
+ crictl: UNUSED
+ - runtime: Kubernetes-1.17
+ version: 1.17.14-00
+ crictl: UNUSED
+ - runtime: Kubernetes-1.18
+ version: 1.18.12-00
+ crictl: UNUSED
+ - runtime: Kubernetes-1.19
+ version: 1.19.4-00
+ crictl: UNUSED
+ - runtime: Kubernetes-1.20
+ version: 1.20.1-00
+ crictl: UNUSED
+ - runtime: Kubernetes-1.21
+ version: 1.21.0-00
+ crictl: UNUSED
+ test:
+ - case: end-to-end
+ file: test/run-end-to-end.py
+ - case: webhook
+ file: test/run-webhook.py
steps:
- - name: Checkout the head commit of the branch
- uses: actions/checkout@v2
- with:
- persist-credentials: false
+ - name: Checkout the head commit of the branch
+ uses: actions/checkout@v2
+ with:
+ persist-credentials: false
+
+ - name: Setup Python
+ uses: actions/setup-python@v2
+ with:
+ python-version: 3.8
+ - name: Install Python kubernetes dependency
+ run: |
+ python -m pip install --upgrade pip
+ pip install kubernetes
- - name: Setup Python
- uses: actions/setup-python@v2
- with:
- python-version: 3.8
- - name: Install Python kubernetes dependency
- run: |
- python -m pip install --upgrade pip
- pip install kubernetes
+ - name: Download Agent container artifact
+ if: startsWith(github.event_name, 'pull_request')
+ uses: actions/download-artifact@v2
+ with:
+ name: agent.tar
+ - name: Download Controller container artifact
+ if: startsWith(github.event_name, 'pull_request')
+ uses: actions/download-artifact@v2
+ with:
+ name: controller.tar
+ - name: Download Webhook-Configuration container artifact
+ if: startsWith(github.event_name, 'pull_request')
+ uses: actions/download-artifact@v2
+ with:
+ name: webhook-configuration.tar
- - name: Download Agent container artifact
- if: startsWith(github.event_name, 'pull_request')
- uses: actions/download-artifact@v2
- with:
- name: agent.tar
- - name: Download Controller container artifact
- if: startsWith(github.event_name, 'pull_request')
- uses: actions/download-artifact@v2
- with:
- name: controller.tar
+ - if: startsWith(matrix.kube.runtime, 'K3s')
+ name: Install K3s
+ env:
+ INSTALL_K3S_VERSION: ${{ matrix.kube.version }}
+ run: |
+ sudo curl -sfL https://get.k3s.io -o install.sh
+ sudo chmod +x install.sh
+ ./install.sh server --kubelet-arg=eviction-hard="imagefs.available<1%,nodefs.available<1%" --kubelet-arg=eviction-minimum-reclaim="imagefs.available=1%,nodefs.available=1%"
+ sudo addgroup k3s-admin
+ sudo adduser $USER k3s-admin
+ sudo usermod -a -G k3s-admin $USER
+ sudo chgrp k3s-admin /etc/rancher/k3s/k3s.yaml
+ sudo chmod g+r /etc/rancher/k3s/k3s.yaml
+ sudo chmod 666 /etc/rancher/k3s/*
+ mkdir -p ~/.kube/ && cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
+ curl -L https://github.com/kubernetes-sigs/cri-tools/releases/download/${{ matrix.kube.crictl }}/crictl-${{ matrix.kube.crictl }}-linux-amd64.tar.gz --output crictl-${{ matrix.kube.crictl }}-linux-amd64.tar.gz
+ sudo tar zxvf crictl-${{ matrix.kube.crictl }}-linux-amd64.tar.gz -C /usr/local/bin
+ rm -f crictl-${{ matrix.kube.crictl }}-linux-amd64.tar.gz
+ echo "--set agent.host.crictl=/usr/local/bin/crictl --set agent.host.dockerShimSock=/run/k3s/containerd/containerd.sock" > /tmp/cri_args_to_test.txt
+ echo 'kubectl' > /tmp/runtime_cmd_to_test.txt
+ echo '~/.kube/config' > /tmp/kubeconfig_path_to_test.txt
+ until kubectl get node ${HOSTNAME,,} -o jsonpath='{@.metadata.name}:{range @.status.conditions[*]}{@.type}={@.status}' | grep 'Ready=True'; do echo "waiting for k3s to become ready"; sleep 10; done
+ if [ "${{ matrix.kube.runtime }}" == "K3s-1.16" ]; then
+ mkdir -p /var/lib/kubelet
+ if [ -d /var/lib/kubelet/device-plugins ]; then
+ sudo rm -rf /var/lib/kubelet/device-plugins
+ fi
+ sudo ln -s /var/lib/rancher/k3s/agent/kubelet/device-plugins /var/lib/kubelet/device-plugins
+ fi
- - if: startsWith(matrix.kube-runtime, 'K3s')
- name: Install K3s
- env:
- INSTALL_K3S_VERSION: ${{ matrix.kube-version }}
- run: |
- sudo curl -sfL https://get.k3s.io | sh -
- sudo addgroup k3s-admin
- sudo adduser $USER k3s-admin
- sudo usermod -a -G k3s-admin $USER
- sudo chgrp k3s-admin /etc/rancher/k3s/k3s.yaml
- sudo chmod g+r /etc/rancher/k3s/k3s.yaml
- sudo chmod 666 /etc/rancher/k3s/*
- mkdir -p ~/.kube/ && cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
- VERSION="v1.17.0"
- curl -L https://github.com/kubernetes-sigs/cri-tools/releases/download/$VERSION/crictl-${VERSION}-linux-amd64.tar.gz --output crictl-${VERSION}-linux-amd64.tar.gz
- sudo tar zxvf crictl-$VERSION-linux-amd64.tar.gz -C $(pwd)
- rm -f crictl-$VERSION-linux-amd64.tar.gz
- echo '--set agent.host.crictl=$(pwd)/crictl --set agent.host.dockerShimSock=/run/k3s/containerd/containerd.sock' > /tmp/cri_args_to_test.txt
- echo 'kubectl' > /tmp/runtime_cmd_to_test.txt
- echo '~/.kube/config' > /tmp/kubeconfig_path_to_test.txt
- until kubectl get node ${HOSTNAME,,} -o jsonpath='{@.metadata.name}:{range @.status.conditions[*]}{@.type}={@.status}' | grep 'Ready=True'; do echo "waiting for k3s to become ready"; sleep 10; done
+ - if: (startsWith(github.event_name, 'pull_request')) && (startsWith(matrix.kube.runtime, 'K3s'))
+ name: Import local agent and controller to K3s
+ run: |
+ sudo k3s ctr image import agent.tar
+ sudo k3s ctr image import controller.tar
+ sudo k3s ctr image import webhook-configuration.tar
- - if: (startsWith(github.event_name, 'pull_request')) && (startsWith(matrix.kube-runtime, 'K3s'))
- name: Import local agent and controller to K3s
- run: |
- sudo k3s ctr image import agent.tar
- sudo k3s ctr image import controller.tar
+ - if: startsWith(matrix.kube.runtime, 'Kubernetes')
+ name: Install Kubernetes
+ run: |
+ sudo apt-get update -y
+ sudo apt-get install -o Dpkg::Options::="--force-overwrite" -y --allow-downgrades kubelet=${{ matrix.kube.version }} kubeadm=${{ matrix.kube.version }} kubectl=${{ matrix.kube.version }}
+ kubectl version && echo "kubectl return code: $?" || echo "kubectl return code: $?"
+ kubeadm version && echo "kubeadm return code: $?" || echo "kubeadm return code: $?"
+ kubelet --version && echo "kubelet return code: $?" || echo "kubelet return code: $?"
+ sudo swapoff -a
+ sudo kubeadm init
+ sudo mkdir -p $HOME/.kube
+ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
+ sudo chown $(id -u):$(id -g) $HOME/.kube/config
+ kubectl taint nodes --all node-role.kubernetes.io/master-
+ echo '--set agent.host.crictl=/usr/bin/crictl --set agent.host.dockerShimSock=/var/run/dockershim.sock' > /tmp/cri_args_to_test.txt
+ echo 'kubectl' > /tmp/runtime_cmd_to_test.txt
+ echo '~/.kube/config' > /tmp/kubeconfig_path_to_test.txt
+ until kubectl get node ${HOSTNAME,,} -o jsonpath='{@.metadata.name}:{range @.status.conditions[*]}{@.type}={@.status}' | grep 'Ready=True'; do echo "waiting for kubernetes to become ready"; sleep 10; done
- - if: startsWith(matrix.kube-runtime, 'Kubernetes')
- name: Install Kubernetes
- run: |
- sudo apt-get update -y
- sudo apt-get install -y --allow-downgrades kubelet=${{ matrix.kube-version }} kubeadm=${{ matrix.kube-version }} kubectl=${{ matrix.kube-version }}
- kubectl version && echo "kubectl return code: $?" || echo "kubectl return code: $?"
- kubeadm version && echo "kubeadm return code: $?" || echo "kubeadm return code: $?"
- kubelet --version && echo "kubelet return code: $?" || echo "kubelet return code: $?"
- sudo swapoff -a
- sudo kubeadm init
- sudo mkdir -p $HOME/.kube
- sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
- sudo chown $(id -u):$(id -g) $HOME/.kube/config
- kubectl taint nodes --all node-role.kubernetes.io/master-
- echo '--set agent.host.crictl=/usr/bin/crictl --set agent.host.dockerShimSock=/var/run/dockershim.sock' > /tmp/cri_args_to_test.txt
- echo 'kubectl' > /tmp/runtime_cmd_to_test.txt
- echo '~/.kube/config' > /tmp/kubeconfig_path_to_test.txt
- until kubectl get node ${HOSTNAME,,} -o jsonpath='{@.metadata.name}:{range @.status.conditions[*]}{@.type}={@.status}' | grep 'Ready=True'; do echo "waiting for kubernetes to become ready"; sleep 10; done
+ - if: (startsWith(github.event_name, 'pull_request')) && (startsWith(matrix.kube.runtime, 'Kubernetes'))
+ name: Import local agent and controller to Kubernetes
+ run: |
+ sudo docker load --input agent.tar
+ sudo docker load --input controller.tar
+ sudo docker load --input webhook-configuration.tar
- - if: (startsWith(github.event_name, 'pull_request')) && (startsWith(matrix.kube-runtime, 'Kubernetes'))
- name: Import local agent and controller to Kubernetes
- run: |
- sudo docker load --input agent.tar
- sudo docker load --input controller.tar
+ - if: startsWith(matrix.kube.runtime, 'MicroK8s')
+ name: Install MicroK8s
+ run: |
+ set -x
+ sudo snap install microk8s --classic --channel=${{ matrix.kube.version }}
+ sudo microk8s status --wait-ready
+ sudo usermod -a -G microk8s $USER
+ sudo ls -la $HOME/.kube
+ echo sudo chown $(id -u):$(id -g) $HOME/.kube
+ sudo chown -f -R $USER $HOME/.kube --verbose
+ sudo sh -c "microk8s.kubectl config view --raw >~/.kube/config"
+ sudo cat ~/.kube/config
+ sudo microk8s.enable rbac dns
+ sudo sed -i 's/memory.available<100Mi,nodefs.available<1Gi,imagefs.available<1Gi/memory.available<25Mi,nodefs.available<50Mi,imagefs.available<50Mi/' /var/snap/microk8s/current/args/kubelet
+ sudo systemctl restart snap.microk8s.daemon-kubelet
+ until sudo microk8s.status --wait-ready; do sleep 5s; echo "Try again"; done
+ curl -L https://github.com/kubernetes-sigs/cri-tools/releases/download/${{ matrix.kube.crictl }}/crictl-${{ matrix.kube.crictl }}-linux-amd64.tar.gz --output crictl-${{ matrix.kube.crictl }}-linux-amd64.tar.gz
+ sudo tar zxvf crictl-${{ matrix.kube.crictl }}-linux-amd64.tar.gz -C /usr/local/bin
+ rm -f crictl-${{ matrix.kube.crictl }}-linux-amd64.tar.gz
+ echo '--set agent.host.crictl=/usr/local/bin/crictl --set agent.host.dockerShimSock=/var/snap/microk8s/common/run/containerd.sock' > /tmp/cri_args_to_test.txt
+ echo 'microk8s kubectl' > /tmp/runtime_cmd_to_test.txt
+ echo '~/.kube/config' > /tmp/kubeconfig_path_to_test.txt
- - if: startsWith(matrix.kube-runtime, 'MicroK8s')
- name: Install MicroK8s
- run: |
- sudo snap install microk8s --classic --channel=${{ matrix.kube-version }}
- sudo microk8s status --wait-ready
- sudo usermod -a -G microk8s $USER
- sudo ls -la $HOME/.kube
- echo sudo chown $(id -u):$(id -g) $HOME/.kube
- sudo chown -f -R $USER $HOME/.kube --verbose
- sudo sh -c "microk8s.kubectl config view --raw >~/.kube/config"
- sudo cat ~/.kube/config
- sudo microk8s.enable helm3
- sudo microk8s.enable rbac
- sudo microk8s.enable dns
- sudo microk8s.status --wait-ready
- VERSION="v1.17.0"
- curl -L https://github.com/kubernetes-sigs/cri-tools/releases/download/$VERSION/crictl-${VERSION}-linux-amd64.tar.gz --output crictl-${VERSION}-linux-amd64.tar.gz
- sudo tar zxvf crictl-$VERSION-linux-amd64.tar.gz -C /usr/local/bin
- rm -f crictl-$VERSION-linux-amd64.tar.gz
- echo '--set agent.host.crictl=/usr/local/bin/crictl --set agent.host.dockerShimSock=/var/snap/microk8s/common/run/containerd.sock' > /tmp/cri_args_to_test.txt
- echo 'microk8s kubectl' > /tmp/runtime_cmd_to_test.txt
- echo '~/.kube/config' > /tmp/kubeconfig_path_to_test.txt
+ - if: (startsWith(github.event_name, 'pull_request')) && (startsWith(matrix.kube.runtime, 'MicroK8s'))
+ name: Import local agent and controller to MicroK8s
+ run: |
+ sudo microk8s.status --wait-ready
+ until sudo microk8s ctr images ls; do sleep 5s; echo "Try again"; done
+ sudo microk8s ctr images ls
+ sudo microk8s ctr --debug --timeout 60s images import agent.tar
+ sudo microk8s ctr --debug --timeout 60s images import controller.tar
+ sudo microk8s ctr --debug --timeout 60s images import webhook-configuration.tar
+ sudo microk8s ctr images ls
- - if: (startsWith(github.event_name, 'pull_request')) && (startsWith(matrix.kube-runtime, 'MicroK8s'))
- name: Import local agent and controller to MicroK8s
- run: |
- sleep 15 # 60, 30, 15, and 5 all work in simple tests ... no sleep fails for 1.19.3
- sudo microk8s.status --wait-ready
- sudo microk8s ctr images ls
- sudo microk8s ctr --debug --timeout 10s images import agent.tar
- sudo microk8s ctr --debug --timeout 10s images import controller.tar
- sudo microk8s ctr images ls
+ - name: Add Akri Helm Chart
+ run: helm repo add akri-helm-charts https://deislabs.github.io/akri/
- - name: Add Akri Helm Chart
- run: helm repo add akri-helm-charts https://deislabs.github.io/akri/
-
- # For push and release, we need to wait for the Helm chart and
- # associated containers to build.
- - if: github.event_name == 'push' || github.event_name == 'release'
- name: Set sleep duration before running script to 1500
- run: echo 1500 > /tmp/sleep_duration.txt
+ # For push and release, we need to wait for the Helm chart and
+ # associated containers to build.
+ - if: github.event_name == 'push' || github.event_name == 'release'
+ name: Set sleep duration before running script to 2700
+ run: echo 2700 > /tmp/sleep_duration.txt
- # For pull_request, use the locally built containers.
- - if: startsWith(github.event_name, 'pull_request')
- name: Tell Helm to use the 'local' labels for container images
- run: |
- git fetch origin main
- git show origin/main:version.txt > /tmp/version_to_test.txt
- echo '--set agent.image.pullPolicy=Never,agent.image.tag=pr-amd64,controller.image.pullPolicy=Never,controller.image.tag=pr-amd64' > /tmp/extra_helm_args.txt
- # For non-PR (i.e. push, release, manual), version.txt is corresponds
- # to an existing Helm chart.
- - if: (!(startsWith(github.event_name, 'pull_request')))
- name: Use current version for push
- run: cat version.txt > /tmp/version_to_test.txt
+ # For pull_request, use the locally built containers.
+ - if: startsWith(github.event_name, 'pull_request')
+ name: Tell Helm to use the 'local' labels for container images
+ run: |
+ git fetch origin main
+ git show origin/main:version.txt > /tmp/version_to_test.txt
+ echo '--set agent.image.pullPolicy=Never,agent.image.tag=pr-amd64,controller.image.pullPolicy=Never,controller.image.tag=pr-amd64,webhookConfiguration.image.pullPolicy=Never,webhookConfiguration.image.tag=pr-amd64' > /tmp/extra_helm_args.txt
+ # For non-PR (i.e. push, release, manual), version.txt is corresponds
+ # to an existing Helm chart.
+ - if: (!(startsWith(github.event_name, 'pull_request')))
+ name: Use current version for push
+ run: cat version.txt > /tmp/version_to_test.txt
- # For workflow_dispatch and pull_request, use the files in deployment/helm
- # as basis for helm install ... this enables us to test any changes made to
- # the helm chart files in a PR (where no helm chart is published)
- - if: github.event_name != 'push' && github.event_name != 'release'
- name: Tell Helm to use the files in deployment/helm to build chart
- run: |
- echo './deployment/helm' > /tmp/helm_chart_location.txt
- # For push, use a specific version of the `akri-dev` charts that are built and
- # published by the helm workflow.
- - if: github.event_name == 'push'
- name: Tell Helm to use the `akri-dev` published charts
- run: |
- echo "akri-helm-charts/akri-dev --version $(cat /tmp/version_to_test.txt)" > /tmp/helm_chart_location.txt
- # For release, use a specific version of the `akri` charts that are built and
- # published by the helm workflow.
- - if: github.event_name == 'release'
- name: Tell Helm to use the `akri` published charts
- run: |
- echo "akri-helm-charts/akri --version $(cat /tmp/version_to_test.txt)" > /tmp/helm_chart_location.txt
+ # For workflow_dispatch and pull_request, use the files in deployment/helm
+ # as basis for helm install ... this enables us to test any changes made to
+ # the helm chart files in a PR (where no helm chart is published)
+ - if: github.event_name != 'push' && github.event_name != 'release'
+ name: Tell Helm to use the files in deployment/helm to build chart
+ run: |
+ echo './deployment/helm' > /tmp/helm_chart_location.txt
+ # For push, use a specific version of the `akri-dev` charts that are built and
+ # published by the helm workflow.
+ - if: github.event_name == 'push'
+ name: Tell Helm to use the `akri-dev` published charts
+ run: |
+ echo "akri-helm-charts/akri-dev --version $(cat /tmp/version_to_test.txt)" > /tmp/helm_chart_location.txt
+ # For release, use a specific version of the `akri` charts that are built and
+ # published by the helm workflow.
+ - if: github.event_name == 'release'
+ name: Tell Helm to use the `akri` published charts
+ run: |
+ echo "akri-helm-charts/akri --version $(cat /tmp/version_to_test.txt)" > /tmp/helm_chart_location.txt
- - name: Execute test script ${{ matrix.test-file }}
- run: python ${{ matrix.test-file }}
- - name: Upload Agent log as artifact
- if: always()
- uses: actions/upload-artifact@v2
- with:
- name: ${{ matrix.kube-runtime }}-${{ matrix.test-case }}-agent-log
- path: /tmp/agent_log.txt
- - name: Upload controller log as artifact
- if: always()
- uses: actions/upload-artifact@v2
- with:
- name: ${{ matrix.kube-runtime }}-${{ matrix.test-case }}-controller-log
- path: /tmp/controller_log.txt
+ - name: Execute test script ${{ matrix.test.file }}
+ run: python ${{ matrix.test.file }}
+ - name: Upload Agent log as artifact
+ if: always()
+ uses: actions/upload-artifact@v2
+ with:
+ name: ${{ matrix.kube.runtime }}-${{ matrix.test.case }}-agent-log
+ path: /tmp/agent_log.txt
+ - name: Upload controller log as artifact
+ if: always()
+ uses: actions/upload-artifact@v2
+ with:
+ name: ${{ matrix.kube.runtime }}-${{ matrix.test.case }}-controller-log
+ path: /tmp/controller_log.txt
+ - name: Upload webhook log as artifact
+ if: always()
+ uses: actions/upload-artifact@v2
+ with:
+ name: ${{ matrix.kube.runtime }}-${{ matrix.test.case }}-webhook-log
+ path: /tmp/webhook_log.txt
diff --git a/.github/workflows/security-audit.yml b/.github/workflows/security-audit.yml
new file mode 100644
index 000000000..efb05c301
--- /dev/null
+++ b/.github/workflows/security-audit.yml
@@ -0,0 +1,43 @@
+# This is a security audit workflow that runs security audit checks and send an email in case any vulnerabilities are detected.
+
+name: Security Audit
+on:
+ schedule:
+ - cron: '0 0 * * *' #runs daily at 12:00 am UTC
+
+jobs:
+ security_audit:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - name: Run security audit check
+ id: cargo-audit
+ if: github.repository == 'deislabs/akri' # only run on main repo and not forks
+ continue-on-error: true
+ uses: actions-rs/audit-check@v1
+ with:
+ # token is only used for creating the audit report and does not impact the
+ # functionality or success/failure of the job in case the token is unavailable
+ token: ${{ secrets.GITHUB_TOKEN }}
+
+ # sends an email if security audit failed
+ - name: Send mail
+ if: steps.cargo-audit.outcome != 'success' && github.repository == 'deislabs/akri' # only run on main repo and not forks
+ uses: dawidd6/action-send-mail@v2
+ with:
+ server_address: smtp-mail.outlook.com
+ server_port: 587
+ username: ${{secrets.AKRI_BOT_EMAIL}}
+ password: ${{secrets.AKRI_BOT_PASSWORD}}
+ subject: "Security vulnerability detected in ${{github.repository}}"
+ body: |-
+ A security vulnerability was detected in one or more of Akri's dependencies. For more details, check the output of the [security audit workflow](https://github.com/${{github.repository}}/actions/runs/${{github.run_id}})
+
+ Hint: In most cases, running the [auto-update dependencies](https://github.com/deislabs/akri/actions/workflows/auto-update-dependencies.yml) workflow will fix the issue.
+
+ -Your friendly Akri bot 🤖
+ to: ${{secrets.AKRI_TEAM_EMAIL}}
+ from: ${{secrets.AKRI_BOT_EMAIL}}
+ content_type: text/html
+ convert_markdown: true
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4ac27b876..1c3ee29b4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,128 @@
+# v0.6.5
+
+## Announcing Akri v0.6.5!
+Akri v0.6.5 is a pre-release of Akri.
+
+To find out more about Akri, check out our [README](https://github.com/deislabs/akri/blob/v0.6.5/README.md) and start [contributing](https://github.com/deislabs/akri/blob/v0.6.5/docs/contributing.md) today!
+
+## New Features
+The v0.6.5 release introduces Akri's Logo, new features such as a new extensibility model for Discovery Handlers and a Configuration validating webhook, DevOps improvements, and more.
+
+**New Discovery Handler extensibility model**
+* feat: Discovery Handlers now live behind a [gRPC interface](https://github.com/deislabs/akri/blob/v0.6.5/discovery-utils/proto/discovery.proto) (https://github.com/deislabs/akri/pull/252), so Discovery Handlers can be written in any language without forking Akri and working within its code. See the [Discovery Handler development document] to get started creating a Discovery Handler.
+* feat: Support of both default "slim" and old "full" Agent images (https://github.com/deislabs/akri/pull/279). Prior to this release, the Agent contained udev, ONVIF, and OPC UA Discovery Handlers. As of this release, Akri is moving towards a default of having no embedded Discovery Handlers in the Agent; rather, the desired Discovery Handlers can be deployed separately using Akri's Helm chart. This decreases the attack surface of the Agent and will keep it from exponential growth as new Discovery Handlers are continually supported. Discovery Handlers written in Rust can be conditionally compiled into the Agent -- reference [the development documentation for more details](https://github.com/deislabs/akri/blob/v0.6.5/docs/development.md#local-builds-and-tests). For the time being, Akri will continue to support a an Agent image with udev, ONVIF, and OPC UA Discovery Handlers. It will be used if `agent.full=true` is set when installing Akri's Helm chart.
+* feat: Updates to Akri's Helm charts with templates for Akri's Discovery Handlers and renaming of values to better fit the new model.
+
+DevOps improvements
+* feat: Workflow to auto-update dependencies (https://github.com/deislabs/akri/pull/224)
+* feat: Security audit workflow (https://github.com/deislabs/akri/pull/264)
+* feat: Workflow for canceling previously running workflows on PRs, reducing environmental footprint and queuing of GitHub Actions (https://github.com/deislabs/akri/pull/284)
+* feat: Build all rust components in one workflow instead of previous strategy for a workflow for each build (https://github.com/deislabs/akri/pull/270)
+* fix: More exhaustive linting of Akri Helm charts (https://github.com/deislabs/akri/pull/306)
+
+Other enhancements
+* feat: [**Webhook for validating Configurations**](https://github.com/deislabs/akri/blob/v0.6.5/webhooks/validating/configuration/README.md) (https://github.com/deislabs/akri/pull/206)
+* feat: Support for Akri monitoring via Prometheus (https://github.com/deislabs/akri/pull/190)
+
+Misc
+* feat: **Akri Logo** (https://github.com/deislabs/akri/pull/149)
+* fix: Allow overwriting Controller's `nodeSelectors` (https://github.com/deislabs/akri/pull/194)
+* fix: Updated `mockall` version (https://github.com/deislabs/akri/pull/214)
+* fix: Changed default image `PullPolicy` from `Always` to Kubernetes default (`IfNotPresent`) (https://github.com/deislabs/akri/pull/207)
+* fix: Improved video streaming application (for udev demo) that polls for new service creation (https://github.com/deislabs/akri/pull/173)
+* fix: Patched anomaly detection application (for OPC UA demo) to show values from all brokers (https://github.com/deislabs/akri/pull/229)
+* feat: Timestamped labels for local container builds (https://github.com/deislabs/akri/pull/234)
+* fix: Removed udev directory mount from Agent DaemonSet (https://github.com/deislabs/akri/pull/304)
+* fix: Modified Debug Echo Discovery Handler to specify `Device.properties` and added check to e2e tests (https://github.com/deislabs/akri/pull/288)
+* feat: Support for specifying environment variables broker Pods via a Configuration's `brokerProperties`.
+* fix: Default memory and CPU resource requests and limits for Akri containers (https://github.com/deislabs/akri/pull/305)
+
+View the [full change log](https://github.com/deislabs/akri/compare/v0.1.5...v0.6.5)
+
+## Breaking Changes
+Akri's Configuration and Instance CRDs were modified. The old version of the CRDs should be deleted with `kubectl delete instances.akri.sh configurations.akri.sh`, and the new ones will be applied with a new Akri Helm installation.
+* Akri's Configuration CRD's `protocol` field was replaced with `discoveryHandler` in order to fit Akri's new Discovery Handler extensibility model and make the Configuration no longer strongly tied to Discovery Handlers. It's unused `units` field was removed and `properties` was renamed `brokerProperties` to be more descriptive.
+* Akri's Instance CRD's unused `rbac` field was removed and `metadate` was renamed `brokerProperties` to be more descriptive and aligned with the Configuration CRD.
+
+Significant changes were made to Akri's Helm chart. Consult the latest user guide and Configurations documentation.
+
+By default, the Agent contains no Discovery Handlers. To deploy Discovery Handlers, they must be explicitly enabled in Akri's Helm chart.
+
+## Known Issues
+N/A
+
+## Validated With
+
+| Distribution | Version |
+|---|---|
+| Kubernetes | v1.21.0 |
+| Kubernetes | v1.20.1 |
+| Kubernetes | v1.19.4 |
+| Kubernetes | v1.18.12 |
+| Kubernetes | v1.17.14 |
+| Kubernetes | v1.16.15 |
+| K3s | v1.20.6+k3s1 |
+| K3s | v1.19.10+k3s1 |
+| K3s | v1.18.9+k3s1 |
+| K3s | v1.17.17+k3s1 |
+| K3s | v1.16.14+k3s1 |
+| MicroK8s | 1.21/stable |
+| MicroK8s | 1.20/stable |
+| MicroK8s | 1.19/stable |
+| MicroK8s | 1.18/stable |
+| MicroK8s | 1.17/stable |
+| MicroK8s | 1.16/stable |
+
+## What's next?
+Check out our [roadmap](https://github.com/deislabs/akri/blob/v0.6.5/docs/roadmap.md) to see the features we are looking forward to!
+
+## Release history
+See [CHANGELOG.md](https://github.com/deislabs/akri/blob/v0.6.5/CHANGELOG.md) for more information on what changed in this and previous releases.
+# v0.1.5
+
+## Announcing Akri v0.1.5!
+Akri v0.1.5 is a pre-release of Akri.
+
+To find out more about Akri, check out our [README](https://github.com/deislabs/akri/blob/v0.1.5/README.md) and start [contributing](https://github.com/deislabs/akri/blob/v0.1.5/docs/contributing.md) today!
+
+## New Features
+The v0.1.5 release introduces support for OPC UA discovery along with:
+
+* End to end demo for discovering and utilizing OPC UA servers
+* Sample anomaly detection application for OPC UA demo
+* Sample OPC UA broker
+* OPC UA certificate generator
+
+View the [full change log](https://github.com/deislabs/akri/compare/v0.0.44...v0.1.5)
+
+## Breaking Changes
+N/A
+
+## Known Issues
+N/A
+
+## Validated With
+
+| Distribution | Version |
+|---|---|
+| Kubernetes | v1.20.1 |
+| Kubernetes | v1.19.4 |
+| Kubernetes | v1.18.12 |
+| Kubernetes | v1.17.14 |
+| Kubernetes | v1.16.15 |
+| K3s | v1.20.0+k3s2 |
+| K3s | v1.19.4+k3s1 |
+| K3s | v1.18.9+k3s1 |
+| MicroK8s | 1.20/stable |
+| MicroK8s | 1.19/stable |
+| MicroK8s | 1.18/stable |
+
+## What's next?
+Check out our [roadmap](https://github.com/deislabs/akri/blob/v0.1.5/docs/roadmap.md) to see the features we are looking forward to!
+
+## Release history
+See [CHANGELOG.md](https://github.com/deislabs/akri/blob/v0.1.5/CHANGELOG.md) for more information on what changed in this and previous releases.
+
# v0.0.44
## Announcing Akri v0.0.44!
diff --git a/Cargo.lock b/Cargo.lock
index 658cc977e..c5453fd02 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,127 +1,566 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
-name = "addr2line"
-version = "0.13.0"
+name = "actix"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1be241f88f3b1e7e9a3fbe3b5a8a0f6915b5a1d7ee0d9a248d3376d01068cc60"
+dependencies = [
+ "actix-rt",
+ "actix_derive",
+ "bitflags",
+ "bytes 0.5.6",
+ "crossbeam-channel",
+ "derive_more",
+ "futures-channel",
+ "futures-util",
+ "log",
+ "once_cell",
+ "parking_lot 0.11.1",
+ "pin-project 0.4.28",
+ "smallvec 1.6.1",
+ "tokio 0.2.25",
+ "tokio-util 0.3.1",
+ "trust-dns-proto",
+ "trust-dns-resolver",
+]
+
+[[package]]
+name = "actix-codec"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78d1833b3838dbe990df0f1f87baf640cf6146e898166afe401839d1b001e570"
+dependencies = [
+ "bitflags",
+ "bytes 0.5.6",
+ "futures-core",
+ "futures-sink",
+ "log",
+ "pin-project 0.4.28",
+ "tokio 0.2.25",
+ "tokio-util 0.3.1",
+]
+
+[[package]]
+name = "actix-connect"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "177837a10863f15ba8d3ae3ec12fac1099099529ed20083a27fdfe247381d0dc"
+dependencies = [
+ "actix-codec",
+ "actix-rt",
+ "actix-service",
+ "actix-utils",
+ "derive_more",
+ "either",
+ "futures-util",
+ "http 0.2.3",
+ "log",
+ "openssl",
+ "tokio-openssl",
+ "trust-dns-proto",
+ "trust-dns-resolver",
+]
+
+[[package]]
+name = "actix-http"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "452299e87817ae5673910e53c243484ca38be3828db819b6011736fc6982e874"
+dependencies = [
+ "actix-codec",
+ "actix-connect",
+ "actix-rt",
+ "actix-service",
+ "actix-threadpool",
+ "actix-tls",
+ "actix-utils",
+ "base64 0.13.0",
+ "bitflags",
+ "brotli2",
+ "bytes 0.5.6",
+ "cookie",
+ "copyless",
+ "derive_more",
+ "either",
+ "encoding_rs",
+ "flate2",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "fxhash",
+ "h2 0.2.6",
+ "http 0.2.3",
+ "httparse",
+ "indexmap",
+ "itoa",
+ "language-tags",
+ "lazy_static",
+ "log",
+ "mime",
+ "percent-encoding 2.1.0",
+ "pin-project 1.0.6",
+ "rand 0.7.3",
+ "regex",
+ "serde",
+ "serde_json",
+ "serde_urlencoded 0.7.0",
+ "sha-1 0.9.4",
+ "slab",
+ "time 0.2.26",
+]
+
+[[package]]
+name = "actix-macros"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4ca8ce00b267af8ccebbd647de0d61e0674b6e61185cc7a592ff88772bed655"
+dependencies = [
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "actix-router"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2ad299af73649e1fc893e333ccf86f377751eb95ff875d095131574c6f43452c"
+dependencies = [
+ "bytestring",
+ "http 0.2.3",
+ "log",
+ "regex",
+ "serde",
+]
+
+[[package]]
+name = "actix-rt"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "143fcc2912e0d1de2bcf4e2f720d2a60c28652ab4179685a1ee159e0fb3db227"
+dependencies = [
+ "actix-macros",
+ "actix-threadpool",
+ "copyless",
+ "futures-channel",
+ "futures-util",
+ "smallvec 1.6.1",
+ "tokio 0.2.25",
+]
+
+[[package]]
+name = "actix-server"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45407e6e672ca24784baa667c5d32ef109ccdd8d5e0b5ebb9ef8a67f4dfb708e"
+dependencies = [
+ "actix-codec",
+ "actix-rt",
+ "actix-service",
+ "actix-utils",
+ "futures-channel",
+ "futures-util",
+ "log",
+ "mio 0.6.23",
+ "mio-uds",
+ "num_cpus",
+ "slab",
+ "socket2 0.3.19",
+]
+
+[[package]]
+name = "actix-service"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0052435d581b5be835d11f4eb3bce417c8af18d87ddf8ace99f8e67e595882bb"
+dependencies = [
+ "futures-util",
+ "pin-project 0.4.28",
+]
+
+[[package]]
+name = "actix-testing"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47239ca38799ab74ee6a8a94d1ce857014b2ac36f242f70f3f75a66f691e791c"
+dependencies = [
+ "actix-macros",
+ "actix-rt",
+ "actix-server",
+ "actix-service",
+ "log",
+ "socket2 0.3.19",
+]
+
+[[package]]
+name = "actix-threadpool"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d209f04d002854b9afd3743032a27b066158817965bf5d036824d19ac2cc0e30"
+dependencies = [
+ "derive_more",
+ "futures-channel",
+ "lazy_static",
+ "log",
+ "num_cpus",
+ "parking_lot 0.11.1",
+ "threadpool",
+]
+
+[[package]]
+name = "actix-tls"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24789b7d7361cf5503a504ebe1c10806896f61e96eca9a7350e23001aca715fb"
+dependencies = [
+ "actix-codec",
+ "actix-service",
+ "actix-utils",
+ "futures-util",
+ "openssl",
+ "tokio-openssl",
+]
+
+[[package]]
+name = "actix-utils"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e9022dec56632d1d7979e59af14f0597a28a830a9c1c7fec8b2327eb9f16b5a"
+dependencies = [
+ "actix-codec",
+ "actix-rt",
+ "actix-service",
+ "bitflags",
+ "bytes 0.5.6",
+ "either",
+ "futures-channel",
+ "futures-sink",
+ "futures-util",
+ "log",
+ "pin-project 0.4.28",
+ "slab",
+]
+
+[[package]]
+name = "actix-web"
+version = "3.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e641d4a172e7faa0862241a20ff4f1f5ab0ab7c279f00c2d4587b77483477b86"
+dependencies = [
+ "actix-codec",
+ "actix-http",
+ "actix-macros",
+ "actix-router",
+ "actix-rt",
+ "actix-server",
+ "actix-service",
+ "actix-testing",
+ "actix-threadpool",
+ "actix-tls",
+ "actix-utils",
+ "actix-web-codegen",
+ "awc",
+ "bytes 0.5.6",
+ "derive_more",
+ "encoding_rs",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "fxhash",
+ "log",
+ "mime",
+ "openssl",
+ "pin-project 1.0.6",
+ "regex",
+ "serde",
+ "serde_json",
+ "serde_urlencoded 0.7.0",
+ "socket2 0.3.19",
+ "time 0.2.26",
+ "tinyvec",
+ "url 2.2.1",
+]
+
+[[package]]
+name = "actix-web-codegen"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad26f77093333e0e7c6ffe54ebe3582d908a104e448723eec6d43d08b07143fb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "actix_derive"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072"
+checksum = "b95aceadaf327f18f0df5962fedc1bde2f870566a0b9f65c89508a3b1f79334c"
dependencies = [
- "gimli",
+ "proc-macro2",
+ "quote",
+ "syn",
]
[[package]]
name = "adler"
-version = "0.2.3"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "agent"
-version = "0.1.5"
-dependencies = [
- "akri-shared",
+version = "0.6.5"
+dependencies = [
+ "akri-debug-echo",
+ "akri-discovery-utils 0.6.5",
+ "akri-onvif",
+ "akri-opcua",
+ "akri-shared 0.6.5",
+ "akri-udev",
+ "anyhow",
"async-stream",
"async-trait",
"blake2",
- "cfg-if",
+ "cfg-if 1.0.0",
"chrono",
- "env_logger",
- "failure",
- "futures 0.1.29",
- "futures 0.3.5",
+ "env_logger 0.8.3",
+ "futures 0.3.14",
"futures-core",
"futures-util",
- "hyper 0.11.27",
+ "h2 0.2.6",
+ "hyper 0.13.10",
"k8s-openapi",
"kube",
- "log 0.4.8",
- "mime",
+ "lazy_static",
+ "log",
+ "mock_instant",
"mockall",
- "opcua-client",
- "pest",
- "pest_derive",
+ "mockall_double",
+ "prometheus 0.12.0",
"prost",
- "rand 0.7.3",
- "regex 1.3.9",
+ "rand 0.8.3",
"serde",
"serde_derive",
"serde_json",
"serde_yaml",
- "sxd-document",
- "sxd-xpath",
"tempfile",
- "tokio 0.2.21",
+ "tokio 0.2.25",
"tokio-core",
"tonic",
"tonic-build",
"tower",
- "udev",
- "url 2.1.1",
- "uuid 0.8.1",
- "xml-rs",
- "yaserde",
- "yaserde_derive",
+ "url 2.2.1",
+ "uuid",
]
[[package]]
name = "aho-corasick"
-version = "0.6.10"
+version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5"
+checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
dependencies = [
"memchr",
]
[[package]]
-name = "aho-corasick"
-version = "0.7.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86"
+name = "akri-debug-echo"
+version = "0.6.5"
dependencies = [
- "memchr",
+ "akri-discovery-utils 0.6.5",
+ "akri-shared 0.6.5",
+ "anyhow",
+ "async-trait",
+ "env_logger 0.8.3",
+ "futures-util",
+ "log",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "serde_yaml",
+ "tokio 0.2.25",
+ "tonic",
]
[[package]]
-name = "akri-shared"
-version = "0.1.5"
+name = "akri-discovery-utils"
+version = "0.6.3"
+source = "git+https://github.com/deislabs/akri?branch=main#26794100674e3e2d3899ed8570a284181c6eaa00"
+dependencies = [
+ "akri-shared 0.6.3",
+ "anyhow",
+ "futures 0.3.14",
+ "log",
+ "prost",
+ "serde",
+ "serde_derive",
+ "serde_yaml",
+ "tokio 0.2.25",
+ "tonic",
+ "tonic-build",
+ "tower",
+]
+
+[[package]]
+name = "akri-discovery-utils"
+version = "0.6.5"
dependencies = [
+ "akri-shared 0.6.5",
+ "anyhow",
+ "async-trait",
+ "futures 0.3.14",
+ "log",
+ "prost",
+ "serde",
+ "serde_derive",
+ "serde_yaml",
+ "tempfile",
+ "tokio 0.2.25",
+ "tonic",
+ "tonic-build",
+ "tower",
+]
+
+[[package]]
+name = "akri-onvif"
+version = "0.6.5"
+dependencies = [
+ "akri-discovery-utils 0.6.5",
+ "akri-shared 0.6.5",
+ "anyhow",
"async-trait",
"bytes 0.5.6",
+ "env_logger 0.8.3",
+ "futures-util",
+ "hyper 0.13.10",
+ "log",
+ "mockall",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "serde_yaml",
+ "sxd-document",
+ "sxd-xpath",
+ "tokio 0.2.25",
+ "tonic",
+ "uuid",
+ "xml-rs",
+ "yaserde",
+ "yaserde_derive",
+]
+
+[[package]]
+name = "akri-opcua"
+version = "0.6.5"
+dependencies = [
+ "akri-discovery-utils 0.6.5",
+ "akri-shared 0.6.5",
+ "anyhow",
+ "async-trait",
+ "env_logger 0.8.3",
+ "futures-util",
+ "log",
+ "mockall",
+ "opcua-client",
+ "prost",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "serde_yaml",
+ "tokio 0.2.25",
+ "tonic",
+ "url 2.2.1",
+]
+
+[[package]]
+name = "akri-shared"
+version = "0.6.3"
+source = "git+https://github.com/deislabs/akri?branch=main#26794100674e3e2d3899ed8570a284181c6eaa00"
+dependencies = [
+ "anyhow",
+ "async-trait",
"either",
- "env_logger",
- "failure",
- "futures 0.1.29",
- "futures 0.3.5",
+ "env_logger 0.6.2",
+ "futures 0.1.31",
+ "futures 0.3.14",
"futures-util",
- "hyper 0.11.27",
- "hyper 0.13.7",
"k8s-openapi",
"kube",
- "log 0.4.8",
+ "log",
"mockall",
+ "prometheus 0.11.0",
"rand 0.7.3",
"serde",
"serde_derive",
"serde_json",
"serde_yaml",
- "sxd-document",
- "sxd-xpath",
- "tokio 0.2.21",
+ "tokio 0.2.25",
"tokio-core",
"tokio-signal",
+ "tonic",
+ "tower",
+ "warp",
]
[[package]]
-name = "anyhow"
-version = "1.0.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f"
+name = "akri-shared"
+version = "0.6.5"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "either",
+ "env_logger 0.8.3",
+ "futures 0.1.31",
+ "futures 0.3.14",
+ "futures-util",
+ "k8s-openapi",
+ "kube",
+ "log",
+ "mockall",
+ "prometheus 0.11.0",
+ "rand 0.8.3",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "serde_yaml",
+ "tokio 0.2.25",
+ "tokio-core",
+ "tokio-signal",
+ "tonic",
+ "tower",
+ "warp",
+]
[[package]]
-name = "arc-swap"
-version = "0.4.7"
+name = "akri-udev"
+version = "0.6.5"
+dependencies = [
+ "akri-discovery-utils 0.6.5",
+ "anyhow",
+ "async-trait",
+ "env_logger 0.8.3",
+ "futures-util",
+ "log",
+ "mockall",
+ "pest",
+ "pest_derive",
+ "prost",
+ "regex",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "serde_yaml",
+ "tokio 0.2.25",
+ "tonic",
+ "udev",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034"
+checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b"
[[package]]
name = "arrayref"
@@ -131,43 +570,126 @@ checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
[[package]]
name = "arrayvec"
-version = "0.5.1"
+version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
+checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
+
+[[package]]
+name = "async-channel"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319"
+dependencies = [
+ "concurrent-queue",
+ "event-listener",
+ "futures-core",
+]
[[package]]
name = "async-compression"
-version = "0.3.5"
+version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9021768bcce77296b64648cc7a7460e3df99979b97ed5c925c38d1cc83778d98"
+checksum = "b72c1f1154e234325b50864a349b9c8e56939e266a4c307c0f159812df2f9537"
dependencies = [
"bytes 0.5.6",
"flate2",
"futures-core",
"memchr",
- "pin-project-lite",
+ "pin-project-lite 0.2.6",
]
[[package]]
-name = "async-std"
-version = "1.6.2"
+name = "async-executor"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00d68a33ebc8b57800847d00787307f84a562224a14db069b0acefe4c2abbf5d"
+checksum = "eb877970c7b440ead138f6321a3b5395d6061183af779340b65e20c0fede9146"
dependencies = [
"async-task",
- "crossbeam-utils",
+ "concurrent-queue",
+ "fastrand",
+ "futures-lite",
+ "once_cell",
+ "vec-arena",
+]
+
+[[package]]
+name = "async-global-executor"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9586ec52317f36de58453159d48351bc244bc24ced3effc1fce22f3d48664af6"
+dependencies = [
+ "async-channel",
+ "async-executor",
+ "async-io",
+ "async-mutex",
+ "blocking",
+ "futures-lite",
+ "num_cpus",
+ "once_cell",
+]
+
+[[package]]
+name = "async-io"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9315f8f07556761c3e48fec2e6b276004acf426e6dc068b2c2251854d65ee0fd"
+dependencies = [
+ "concurrent-queue",
+ "fastrand",
+ "futures-lite",
+ "libc",
+ "log",
+ "nb-connect",
+ "once_cell",
+ "parking",
+ "polling",
+ "vec-arena",
+ "waker-fn",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "async-lock"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1996609732bde4a9988bc42125f55f2af5f3c36370e27c778d5191a4a1b63bfb"
+dependencies = [
+ "event-listener",
+]
+
+[[package]]
+name = "async-mutex"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e"
+dependencies = [
+ "event-listener",
+]
+
+[[package]]
+name = "async-std"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9f06685bad74e0570f5213741bea82158279a4103d988e57bfada11ad230341"
+dependencies = [
+ "async-channel",
+ "async-global-executor",
+ "async-io",
+ "async-lock",
+ "crossbeam-utils 0.8.3",
"futures-channel",
"futures-core",
"futures-io",
+ "futures-lite",
+ "gloo-timers",
"kv-log-macro",
- "log 0.4.8",
+ "log",
"memchr",
"num_cpus",
"once_cell",
- "pin-project-lite",
+ "pin-project-lite 0.2.6",
"pin-utils",
- "slab 0.4.2",
- "smol",
+ "slab",
"wasm-bindgen-futures",
]
@@ -194,21 +716,27 @@ dependencies = [
[[package]]
name = "async-task"
-version = "3.0.0"
+version = "4.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c17772156ef2829aadc587461c7753af20b7e8db1529bc66855add962a3b35d3"
+checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0"
[[package]]
name = "async-trait"
-version = "0.1.36"
+version = "0.1.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a265e3abeffdce30b2e26b7a11b222fe37c6067404001b434101457d0385eb92"
+checksum = "36ea56748e10732c49404c153638a15ec3d6211ec5ff35d9bb20e13b93576adf"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
+[[package]]
+name = "atomic-waker"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a"
+
[[package]]
name = "atty"
version = "0.2.14"
@@ -222,43 +750,40 @@ dependencies = [
[[package]]
name = "autocfg"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
-
-[[package]]
-name = "backtrace"
-version = "0.3.50"
+version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46254cf2fdcdf1badb5934448c1bcbe046a56537b3987d96c51a7afc5d03f293"
-dependencies = [
- "addr2line",
- "cfg-if",
- "libc",
- "miniz_oxide",
- "object",
- "rustc-demangle",
-]
+checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
-name = "base64"
-version = "0.6.0"
+name = "awc"
+version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9"
+checksum = "b381e490e7b0cfc37ebc54079b0413d8093ef43d14a4e4747083f7fa47a9e691"
dependencies = [
- "byteorder",
- "safemem 0.2.0",
+ "actix-codec",
+ "actix-http",
+ "actix-rt",
+ "actix-service",
+ "base64 0.13.0",
+ "bytes 0.5.6",
+ "cfg-if 1.0.0",
+ "derive_more",
+ "futures-core",
+ "log",
+ "mime",
+ "openssl",
+ "percent-encoding 2.1.0",
+ "rand 0.7.3",
+ "serde",
+ "serde_json",
+ "serde_urlencoded 0.7.0",
]
[[package]]
-name = "base64"
-version = "0.9.3"
+name = "base-x"
+version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643"
-dependencies = [
- "byteorder",
- "safemem 0.3.3",
-]
+checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b"
[[package]]
name = "base64"
@@ -281,6 +806,12 @@ version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
+[[package]]
+name = "base64"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
+
[[package]]
name = "bitflags"
version = "1.2.1"
@@ -289,21 +820,20 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "blake2"
-version = "0.8.1"
+version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330"
+checksum = "10a5720225ef5daecf08657f23791354e1685a8c91a4c60c7f3d3b2892f978f4"
dependencies = [
- "byte-tools",
"crypto-mac",
- "digest",
- "opaque-debug",
+ "digest 0.9.0",
+ "opaque-debug 0.3.0",
]
[[package]]
name = "blake2b_simd"
-version = "0.5.10"
+version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a"
+checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587"
dependencies = [
"arrayref",
"arrayvec",
@@ -319,7 +849,16 @@ dependencies = [
"block-padding",
"byte-tools",
"byteorder",
- "generic-array",
+ "generic-array 0.12.4",
+]
+
+[[package]]
+name = "block-buffer"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
+dependencies = [
+ "generic-array 0.14.4",
]
[[package]]
@@ -333,22 +872,53 @@ dependencies = [
[[package]]
name = "blocking"
-version = "0.4.6"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d17efb70ce4421e351d61aafd90c16a20fb5bfe339fcdc32a86816280e62ce0"
+checksum = "c5e170dbede1f740736619b776d7251cb1b9095c435c34d8ca9f57fcd2f335e9"
dependencies = [
- "futures-channel",
- "futures-util",
+ "async-channel",
+ "async-task",
+ "atomic-waker",
+ "fastrand",
+ "futures-lite",
"once_cell",
- "parking",
- "waker-fn",
+]
+
+[[package]]
+name = "brotli-sys"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4445dea95f4c2b41cde57cc9fee236ae4dbae88d8fcbdb4750fc1bb5d86aaecd"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "brotli2"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0cb036c3eade309815c15ddbacec5b22c4d1f3983a774ab2eac2e3e9ea85568e"
+dependencies = [
+ "brotli-sys",
+ "libc",
+]
+
+[[package]]
+name = "buf_redux"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f"
+dependencies = [
+ "memchr",
+ "safemem",
]
[[package]]
name = "bumpalo"
-version = "3.4.0"
+version = "3.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820"
+checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe"
[[package]]
name = "byte-tools"
@@ -358,9 +928,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
[[package]]
name = "byteorder"
-version = "1.3.4"
+version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bytes"
@@ -378,6 +948,21 @@ version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38"
+[[package]]
+name = "bytes"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040"
+
+[[package]]
+name = "bytestring"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90706ba19e97b90786e19dc0d5e2abd80008d99d4c0c5d1ad0b5e72cec7c494d"
+dependencies = [
+ "bytes 1.0.1",
+]
+
[[package]]
name = "cache-padded"
version = "1.1.1"
@@ -386,9 +971,9 @@ checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba"
[[package]]
name = "cc"
-version = "1.0.58"
+version = "1.0.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518"
+checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
[[package]]
name = "cfg-if"
@@ -396,16 +981,56 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
[[package]]
name = "chrono"
-version = "0.4.13"
+version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c74d84029116787153e02106bf53e66828452a4b325cc8652b788b5967c0a0b6"
+checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [
+ "libc",
"num-integer",
"num-traits",
"serde",
- "time",
+ "time 0.1.44",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "clap"
+version = "3.0.0-beta.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bd1061998a501ee7d4b6d449020df3266ca3124b941ec56cf2005c3779ca142"
+dependencies = [
+ "atty",
+ "bitflags",
+ "clap_derive",
+ "indexmap",
+ "lazy_static",
+ "os_str_bytes",
+ "strsim",
+ "termcolor",
+ "textwrap",
+ "unicode-width",
+ "vec_map",
+]
+
+[[package]]
+name = "clap_derive"
+version = "3.0.0-beta.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "370f715b81112975b1b69db93e0b56ea4cd4e5002ac43b2da8474106a54096a1"
+dependencies = [
+ "heck",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
]
[[package]]
@@ -419,13 +1044,19 @@ dependencies = [
[[package]]
name = "concurrent-queue"
-version = "1.1.1"
+version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f83c06aff61f2d899eb87c379df3cbf7876f14471dcab474e0b6dc90ab96c080"
+checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3"
dependencies = [
"cache-padded",
]
+[[package]]
+name = "const_fn"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "076a6803b0dacd6a88cfe64deba628b01533ff5ef265687e6938280c1afd0a28"
+
[[package]]
name = "constant_time_eq"
version = "0.1.5"
@@ -434,31 +1065,56 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "controller"
-version = "0.1.5"
+version = "0.6.5"
dependencies = [
- "akri-shared",
+ "akri-shared 0.6.5",
+ "anyhow",
"async-std",
"async-trait",
"chrono",
- "env_logger",
- "failure",
- "futures 0.3.5",
+ "env_logger 0.6.2",
+ "futures 0.3.14",
"k8s-openapi",
"kube",
- "log 0.4.8",
+ "lazy_static",
+ "log",
"mockall",
+ "prometheus 0.12.0",
"serde",
"serde_derive",
"serde_json",
"serde_yaml",
- "tokio 0.2.21",
+ "tokio 0.2.25",
]
+[[package]]
+name = "convert_case"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
+
+[[package]]
+name = "cookie"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03a5d7b21829bc7b4bf4754a978a241ae54ea55a40f92bb20216e54096f4b951"
+dependencies = [
+ "percent-encoding 2.1.0",
+ "time 0.2.26",
+ "version_check",
+]
+
+[[package]]
+name = "copyless"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536"
+
[[package]]
name = "core-foundation"
-version = "0.7.0"
+version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171"
+checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62"
dependencies = [
"core-foundation-sys",
"libc",
@@ -466,17 +1122,33 @@ dependencies = [
[[package]]
name = "core-foundation-sys"
-version = "0.7.0"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
+
+[[package]]
+name = "cpuid-bool"
+version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
+checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"
[[package]]
name = "crc32fast"
-version = "1.2.0"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
+dependencies = [
+ "cfg-if 1.0.0",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
+checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87"
dependencies = [
- "cfg-if",
+ "crossbeam-utils 0.7.2",
+ "maybe-uninit",
]
[[package]]
@@ -486,7 +1158,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
dependencies = [
"crossbeam-epoch",
- "crossbeam-utils",
+ "crossbeam-utils 0.7.2",
"maybe-uninit",
]
@@ -497,8 +1169,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
dependencies = [
"autocfg",
- "cfg-if",
- "crossbeam-utils",
+ "cfg-if 0.1.10",
+ "crossbeam-utils 0.7.2",
"lazy_static",
"maybe-uninit",
"memoffset",
@@ -511,8 +1183,8 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
dependencies = [
- "cfg-if",
- "crossbeam-utils",
+ "cfg-if 0.1.10",
+ "crossbeam-utils 0.7.2",
"maybe-uninit",
]
@@ -523,20 +1195,64 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
dependencies = [
"autocfg",
- "cfg-if",
+ "cfg-if 0.1.10",
+ "lazy_static",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49"
+dependencies = [
+ "autocfg",
+ "cfg-if 1.0.0",
"lazy_static",
]
[[package]]
name = "crypto-mac"
-version = "0.7.0"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5"
+checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab"
dependencies = [
- "generic-array",
+ "generic-array 0.14.4",
"subtle",
]
+[[package]]
+name = "ctor"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d"
+dependencies = [
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "debug-echo-discovery-handler"
+version = "0.6.5"
+dependencies = [
+ "akri-debug-echo",
+ "akri-discovery-utils 0.6.5",
+ "env_logger 0.8.3",
+ "log",
+ "tokio 0.2.25",
+]
+
+[[package]]
+name = "derive_more"
+version = "0.99.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f82b1b72f1263f214c0f823371768776c4f5841b942c9883aa8e5ec584fd0ba6"
+dependencies = [
+ "convert_case",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "difference"
version = "2.0.0"
@@ -549,7 +1265,16 @@ version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
dependencies = [
- "generic-array",
+ "generic-array 0.12.4",
+]
+
+[[package]]
+name = "digest"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
+dependencies = [
+ "generic-array 0.14.4",
]
[[package]]
@@ -558,7 +1283,7 @@ version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
dependencies = [
- "cfg-if",
+ "cfg-if 0.1.10",
"dirs-sys",
]
@@ -573,6 +1298,12 @@ dependencies = [
"winapi 0.3.9",
]
+[[package]]
+name = "discard"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
+
[[package]]
name = "downcast"
version = "0.10.0"
@@ -581,23 +1312,35 @@ checksum = "4bb454f0228b18c7f4c3b0ebbee346ed9c52e7443b0999cd543ff3571205701d"
[[package]]
name = "dtoa"
-version = "0.4.6"
+version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b"
+checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
[[package]]
name = "either"
-version = "1.5.3"
+version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
+checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "encoding_rs"
-version = "0.8.23"
+version = "0.8.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065"
+dependencies = [
+ "cfg-if 1.0.0",
+]
+
+[[package]]
+name = "enum-as-inner"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8ac63f94732332f44fe654443c46f6375d1939684c17b0afb6cb56b0456e171"
+checksum = "7c5f0096a91d210159eceb2ff5e1c4da18388a170e1e3ce948aac9c8fdbbf595"
dependencies = [
- "cfg-if",
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
]
[[package]]
@@ -607,33 +1350,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3"
dependencies = [
"atty",
- "humantime",
- "log 0.4.8",
- "regex 1.3.9",
+ "humantime 1.3.0",
+ "log",
+ "regex",
"termcolor",
]
[[package]]
-name = "failure"
-version = "0.1.8"
+name = "env_logger"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
+checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f"
dependencies = [
- "backtrace",
- "failure_derive",
+ "atty",
+ "humantime 2.1.0",
+ "log",
+ "regex",
+ "termcolor",
]
[[package]]
-name = "failure_derive"
-version = "0.1.8"
+name = "event-listener"
+version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
- "synstructure",
-]
+checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59"
[[package]]
name = "fake-simd"
@@ -643,9 +1383,12 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]]
name = "fastrand"
-version = "1.3.3"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36a9cb09840f81cd211e435d00a4e487edd263dc3c8ff815c32dd76ad668ebed"
+checksum = "ca5faf057445ce5c9d4329e382b2ce7ca38550ef3b73a5348362d5f24e0c7fe3"
+dependencies = [
+ "instant",
+]
[[package]]
name = "fixedbitset"
@@ -655,11 +1398,11 @@ checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d"
[[package]]
name = "flate2"
-version = "1.0.16"
+version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68c90b0fc46cf89d227cc78b40e494ff81287a92dd07631e5af0d06fe3cf885e"
+checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"crc32fast",
"libc",
"miniz_oxide",
@@ -667,9 +1410,9 @@ dependencies = [
[[package]]
name = "float-cmp"
-version = "0.6.0"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da62c4f1b81918835a8c6a484a397775fff5953fe83529afd51b05f5c6a6617d"
+checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4"
dependencies = [
"num-traits",
]
@@ -696,16 +1439,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
-name = "fragile"
-version = "0.3.0"
+name = "form_urlencoded"
+version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05f8140122fa0d5dcb9fc8627cfce2b37cc1500f752636d46ea28bc26785c2f9"
+checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
+dependencies = [
+ "matches",
+ "percent-encoding 2.1.0",
+]
[[package]]
-name = "fuchsia-cprng"
-version = "0.1.1"
+name = "fragile"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
+checksum = "69a039c3498dc930fe810151a34ba0c1c70b02b8625035592e74432f678591f2"
[[package]]
name = "fuchsia-zircon"
@@ -725,15 +1472,15 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
[[package]]
name = "futures"
-version = "0.1.29"
+version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef"
+checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678"
[[package]]
name = "futures"
-version = "0.3.5"
+version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613"
+checksum = "a9d5813545e459ad3ca1bff9915e9ad7f1a47dc6a91b627ce321d5863b7dd253"
dependencies = [
"futures-channel",
"futures-core",
@@ -746,9 +1493,9 @@ dependencies = [
[[package]]
name = "futures-channel"
-version = "0.3.5"
+version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5"
+checksum = "ce79c6a52a299137a6013061e0cf0e688fce5d7f1bc60125f520912fdb29ec25"
dependencies = [
"futures-core",
"futures-sink",
@@ -756,25 +1503,15 @@ dependencies = [
[[package]]
name = "futures-core"
-version = "0.3.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399"
-
-[[package]]
-name = "futures-cpupool"
-version = "0.1.8"
+version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
-dependencies = [
- "futures 0.1.29",
- "num_cpus",
-]
+checksum = "098cd1c6dda6ca01650f1a37a794245eb73181d0d4d4e955e2f3c37db7af1815"
[[package]]
name = "futures-executor"
-version = "0.3.5"
+version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314"
+checksum = "10f6cb7042eda00f0049b1d2080aa4b93442997ee507eb3828e8bd7577f94c9d"
dependencies = [
"futures-core",
"futures-task",
@@ -783,15 +1520,30 @@ dependencies = [
[[package]]
name = "futures-io"
-version = "0.3.5"
+version = "0.3.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "365a1a1fb30ea1c03a830fdb2158f5236833ac81fa0ad12fe35b29cddc35cb04"
+
+[[package]]
+name = "futures-lite"
+version = "1.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789"
+checksum = "b4481d0cd0de1d204a4fa55e7d45f07b1d958abcb06714b3446438e2eff695fb"
+dependencies = [
+ "fastrand",
+ "futures-core",
+ "futures-io",
+ "memchr",
+ "parking",
+ "pin-project-lite 0.2.6",
+ "waker-fn",
+]
[[package]]
name = "futures-macro"
-version = "0.3.5"
+version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39"
+checksum = "668c6733a182cd7deb4f1de7ba3bf2120823835b3bcfbeacf7d2c4a773c1bb8b"
dependencies = [
"proc-macro-hack",
"proc-macro2",
@@ -801,18 +1553,15 @@ dependencies = [
[[package]]
name = "futures-sink"
-version = "0.3.5"
+version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc"
+checksum = "5c5629433c555de3d82861a7a4e3794a4c40040390907cfbfd7143a92a426c23"
[[package]]
name = "futures-task"
-version = "0.3.5"
+version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626"
-dependencies = [
- "once_cell",
-]
+checksum = "ba7aa51095076f3ba6d9a1f702f74bd05ec65f555d70d2033d55ba8d69f581bc"
[[package]]
name = "futures-timer"
@@ -822,9 +1571,9 @@ checksum = "a1de7508b218029b0f01662ed8f61b1c964b3ae99d6f25462d0f55a595109df6"
[[package]]
name = "futures-util"
-version = "0.3.5"
+version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6"
+checksum = "3c144ad54d60f23927f0a6b6d816e4271278b64f005ad65e4e35291d2de9c025"
dependencies = [
"futures-channel",
"futures-core",
@@ -833,73 +1582,187 @@ dependencies = [
"futures-sink",
"futures-task",
"memchr",
- "pin-project",
+ "pin-project-lite 0.2.6",
"pin-utils",
"proc-macro-hack",
"proc-macro-nested",
- "slab 0.4.2",
+ "slab",
+]
+
+[[package]]
+name = "fxhash"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
+dependencies = [
+ "byteorder",
]
[[package]]
name = "generic-array"
-version = "0.12.3"
+version = "0.12.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
+dependencies = [
+ "typenum",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
+checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
dependencies = [
"typenum",
+ "version_check",
+]
+
+[[package]]
+name = "gethostname"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e692e296bfac1d2533ef168d0b60ff5897b8b70a4009276834014dd8924cc028"
+dependencies = [
+ "libc",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
+dependencies = [
+ "cfg-if 1.0.0",
+ "libc",
+ "wasi 0.9.0+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
-version = "0.1.14"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
+checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"libc",
- "wasi",
+ "wasi 0.10.0+wasi-snapshot-preview1",
]
[[package]]
-name = "gimli"
-version = "0.22.0"
+name = "gloo-timers"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724"
+checksum = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
[[package]]
name = "h2"
version = "0.2.6"
-source = "git+https://github.com/kate-goldenring/h2#7c7ef6a579c9ce2392787c5728f805ce10f74ddf"
+source = "git+https://github.com/kate-goldenring/h2?branch=master#7c7ef6a579c9ce2392787c5728f805ce10f74ddf"
dependencies = [
"bytes 0.5.6",
"fnv",
"futures-core",
"futures-sink",
"futures-util",
- "http 0.2.1",
+ "http 0.2.3",
"indexmap",
- "slab 0.4.2",
- "tokio 0.2.21",
+ "slab",
+ "tokio 0.2.25",
"tokio-util 0.3.1",
"tracing",
]
+[[package]]
+name = "h2"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc018e188373e2777d0ef2467ebff62a08e66c3f5857b23c8fbec3018210dc00"
+dependencies = [
+ "bytes 1.0.1",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "http 0.2.3",
+ "indexmap",
+ "slab",
+ "tokio 1.4.0",
+ "tokio-util 0.6.5",
+ "tracing",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
+
+[[package]]
+name = "headers"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0b7591fb62902706ae8e7aaff416b1b0fa2c0fd0878b46dc13baa3712d8a855"
+dependencies = [
+ "base64 0.13.0",
+ "bitflags",
+ "bytes 1.0.1",
+ "headers-core",
+ "http 0.2.3",
+ "mime",
+ "sha-1 0.9.4",
+ "time 0.1.44",
+]
+
+[[package]]
+name = "headers-core"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429"
+dependencies = [
+ "http 0.2.3",
+]
+
[[package]]
name = "heck"
-version = "0.3.1"
+version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
+checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "hermit-abi"
-version = "0.1.15"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "hostname"
+version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9"
+checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867"
dependencies = [
"libc",
+ "match_cfg",
+ "winapi 0.3.9",
]
[[package]]
@@ -915,11 +1778,11 @@ dependencies = [
[[package]]
name = "http"
-version = "0.2.1"
+version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9"
+checksum = "7245cd7449cc792608c3c8a9eaf69bd4eabbabf802713748fd739c98b82f0747"
dependencies = [
- "bytes 0.5.6",
+ "bytes 1.0.1",
"fnv",
"itoa",
]
@@ -931,14 +1794,51 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b"
dependencies = [
"bytes 0.5.6",
- "http 0.2.1",
+ "http 0.2.3",
+]
+
+[[package]]
+name = "http-body"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dfb77c123b4e2f72a2069aeae0b4b4949cc7e966df277813fc16347e7549737"
+dependencies = [
+ "bytes 1.0.1",
+ "http 0.2.3",
+ "pin-project-lite 0.2.6",
+]
+
+[[package]]
+name = "http-discovery-handler"
+version = "0.1.0"
+dependencies = [
+ "akri-discovery-utils 0.6.3",
+ "anyhow",
+ "async-trait",
+ "env_logger 0.6.2",
+ "log",
+ "reqwest 0.10.10",
+ "tokio 0.2.25",
+ "tonic",
]
[[package]]
name = "httparse"
-version = "1.3.4"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a1ce40d6fc9764887c2fdc7305c3dcc429ba11ff981c1509416afd5697e4437"
+
+[[package]]
+name = "httpdate"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47"
+
+[[package]]
+name = "httpdate"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"
+checksum = "05842d0d43232b23ccb7060ecb0f0626922c21f30012e97b767b30afd4a5d4b9"
[[package]]
name = "humantime"
@@ -949,55 +1849,58 @@ dependencies = [
"quick-error",
]
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
[[package]]
name = "hyper"
-version = "0.11.27"
+version = "0.13.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34a590ca09d341e94cddf8e5af0bbccde205d5fbc2fa3c09dd67c7f85cea59d7"
+checksum = "8a6f157065790a3ed2f88679250419b5cdd96e714a0d65f7797fd337186e96bb"
dependencies = [
- "base64 0.9.3",
- "bytes 0.4.12",
- "futures 0.1.29",
- "futures-cpupool",
+ "bytes 0.5.6",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "h2 0.2.6",
+ "http 0.2.3",
+ "http-body 0.3.1",
"httparse",
- "iovec",
- "language-tags",
- "log 0.4.8",
- "mime",
- "net2",
- "percent-encoding 1.0.1",
- "relay",
- "time",
- "tokio-core",
- "tokio-io",
- "tokio-proto",
- "tokio-service",
- "unicase",
- "want 0.0.4",
+ "httpdate 0.3.2",
+ "itoa",
+ "pin-project 1.0.6",
+ "socket2 0.3.19",
+ "tokio 0.2.25",
+ "tower-service",
+ "tracing",
+ "want",
]
[[package]]
name = "hyper"
-version = "0.13.7"
+version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e68a8dd9716185d9e64ea473ea6ef63529252e3e27623295a0378a19665d5eb"
+checksum = "1e5f105c494081baa3bf9e200b279e27ec1623895cd504c7dbef8d0b080fcf54"
dependencies = [
- "bytes 0.5.6",
+ "bytes 1.0.1",
"futures-channel",
"futures-core",
"futures-util",
- "h2",
- "http 0.2.1",
- "http-body",
+ "h2 0.3.2",
+ "http 0.2.3",
+ "http-body 0.4.1",
"httparse",
+ "httpdate 1.0.0",
"itoa",
- "pin-project",
- "socket2",
- "time",
- "tokio 0.2.21",
+ "pin-project 1.0.6",
+ "socket2 0.4.0",
+ "tokio 1.4.0",
"tower-service",
"tracing",
- "want 0.3.0",
+ "want",
]
[[package]]
@@ -1007,9 +1910,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d979acc56dcb5b8dddba3917601745e877576475aa046df3226eabdecef78eed"
dependencies = [
"bytes 0.5.6",
- "hyper 0.13.7",
+ "hyper 0.13.10",
"native-tls",
- "tokio 0.2.21",
+ "tokio 0.2.25",
"tokio-tls",
]
@@ -1026,9 +1929,9 @@ dependencies = [
[[package]]
name = "idna"
-version = "0.2.0"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
+checksum = "89829a5d69c23d348314a7ac337fe39173b61149a9864deabd260983aed48c21"
dependencies = [
"matches",
"unicode-bidi",
@@ -1037,11 +1940,30 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "1.4.0"
+version = "1.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe"
+checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3"
dependencies = [
"autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "input_buffer"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19a8a95243d5a0398cae618ec29477c6e3cb631152be5c19481f80bc71559754"
+dependencies = [
+ "bytes 0.5.6",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
+dependencies = [
+ "cfg-if 1.0.0",
]
[[package]]
@@ -1053,6 +1975,24 @@ dependencies = [
"libc",
]
+[[package]]
+name = "ipconfig"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7"
+dependencies = [
+ "socket2 0.3.19",
+ "widestring",
+ "winapi 0.3.9",
+ "winreg 0.6.2",
+]
+
+[[package]]
+name = "ipnet"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135"
+
[[package]]
name = "itertools"
version = "0.8.2"
@@ -1064,15 +2004,15 @@ dependencies = [
[[package]]
name = "itoa"
-version = "0.4.6"
+version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
+checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
[[package]]
name = "js-sys"
-version = "0.3.41"
+version = "0.3.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4b9172132a62451e56142bff9afc91c8e4a4500aa5b847da36815b63bfda916"
+checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c"
dependencies = [
"wasm-bindgen",
]
@@ -1091,7 +2031,7 @@ dependencies = [
"serde",
"serde-value",
"serde_json",
- "url 2.1.1",
+ "url 2.2.1",
]
[[package]]
@@ -1114,20 +2054,20 @@ dependencies = [
"chrono",
"dirs",
"either",
- "futures 0.3.5",
+ "futures 0.3.14",
"futures-timer",
- "http 0.2.1",
+ "http 0.2.3",
"k8s-openapi",
- "log 0.4.8",
+ "log",
"openssl",
- "reqwest",
+ "reqwest 0.10.10",
"serde",
"serde_derive",
"serde_json",
"serde_yaml",
"thiserror",
- "time",
- "url 2.1.1",
+ "time 0.1.44",
+ "url 2.2.1",
]
[[package]]
@@ -1136,7 +2076,7 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
dependencies = [
- "log 0.4.8",
+ "log",
]
[[package]]
@@ -1153,9 +2093,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
-version = "0.2.72"
+version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9f8082297d534141b30c8d39e9b1773713ab50fdbe4ff30f750d063b3bfd701"
+checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
[[package]]
name = "libudev-sys"
@@ -1169,9 +2109,9 @@ dependencies = [
[[package]]
name = "linked-hash-map"
-version = "0.5.3"
+version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
+checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "lock_api"
@@ -1183,21 +2123,31 @@ dependencies = [
]
[[package]]
-name = "log"
-version = "0.3.9"
+name = "lock_api"
+version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
+checksum = "5a3c91c24eae6777794bb1997ad98bbb87daf92890acab859f7eaa4320333176"
dependencies = [
- "log 0.4.8",
+ "scopeguard",
]
[[package]]
name = "log"
-version = "0.4.8"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
+dependencies = [
+ "cfg-if 1.0.0",
+ "value-bag",
+]
+
+[[package]]
+name = "lru-cache"
+version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
+checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
dependencies = [
- "cfg-if",
+ "linked-hash-map",
]
[[package]]
@@ -1206,6 +2156,12 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
+[[package]]
+name = "match_cfg"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
+
[[package]]
name = "matches"
version = "0.1.8"
@@ -1220,15 +2176,15 @@ checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
[[package]]
name = "memchr"
-version = "2.3.3"
+version = "2.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
+checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
[[package]]
name = "memoffset"
-version = "0.5.5"
+version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f"
+checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
dependencies = [
"autocfg",
]
@@ -1251,41 +2207,55 @@ dependencies = [
[[package]]
name = "miniz_oxide"
-version = "0.4.0"
+version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be0f75932c1f6cfae3c04000e40114adf955636e19040f9c0a2c380702aa1c7f"
+checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [
"adler",
+ "autocfg",
]
[[package]]
name = "mio"
-version = "0.6.22"
+version = "0.6.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430"
+checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4"
dependencies = [
- "cfg-if",
+ "cfg-if 0.1.10",
"fuchsia-zircon",
"fuchsia-zircon-sys",
"iovec",
"kernel32-sys",
"libc",
- "log 0.4.8",
- "miow 0.2.1",
+ "log",
+ "miow 0.2.2",
"net2",
- "slab 0.4.2",
+ "slab",
"winapi 0.2.8",
]
+[[package]]
+name = "mio"
+version = "0.7.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956"
+dependencies = [
+ "libc",
+ "log",
+ "miow 0.3.7",
+ "ntapi",
+ "winapi 0.3.9",
+]
+
[[package]]
name = "mio-named-pipes"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656"
dependencies = [
- "log 0.4.8",
- "mio",
- "miow 0.3.5",
+ "log",
+ "mio 0.6.23",
+ "miow 0.3.7",
"winapi 0.3.9",
]
@@ -1297,14 +2267,14 @@ checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0"
dependencies = [
"iovec",
"libc",
- "mio",
+ "mio 0.6.23",
]
[[package]]
name = "miow"
-version = "0.2.1"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
+checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d"
dependencies = [
"kernel32-sys",
"net2",
@@ -1314,21 +2284,29 @@ dependencies = [
[[package]]
name = "miow"
-version = "0.3.5"
+version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e"
+checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
dependencies = [
- "socket2",
"winapi 0.3.9",
]
+[[package]]
+name = "mock_instant"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "717e29a243b81f8130e31e24e04fb151b04a44b5a7d05370935f7d937e9de06d"
+dependencies = [
+ "once_cell",
+]
+
[[package]]
name = "mockall"
-version = "0.6.0"
+version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b95a7e7cfbce0e99ebbf5356a085d3b5e320a7ef300f77cd50a7148aa362e7c2"
+checksum = "18d614ad23f9bb59119b8b5670a85c7ba92c5e9adf4385c81ea00c51c8be33d5"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"downcast",
"fragile",
"lazy_static",
@@ -1339,11 +2317,23 @@ dependencies = [
[[package]]
name = "mockall_derive"
-version = "0.6.0"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dd4234635bca06fc96c7368d038061e0aae1b00a764dc817e900dc974e3deea"
+dependencies = [
+ "cfg-if 1.0.0",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "mockall_double"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5a615a1ad92048ad5d9633251edb7492b8abc057d7a679a9898476aef173935"
+checksum = "3e25b78d34b2b713b1d000d629079755cfc166e6a65f9f4c1c012a94305467c5"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"proc-macro2",
"quote",
"syn",
@@ -1351,19 +2341,37 @@ dependencies = [
[[package]]
name = "multimap"
-version = "0.8.1"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
+
+[[package]]
+name = "multipart"
+version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8883adfde9756c1d30b0f519c9b8c502a94b41ac62f696453c37c7fc0a958ce"
+checksum = "d050aeedc89243f5347c3e237e3e13dc76fbe4ae3742a57b94dc14f69acf76d4"
+dependencies = [
+ "buf_redux",
+ "httparse",
+ "log",
+ "mime",
+ "mime_guess",
+ "quick-error",
+ "rand 0.7.3",
+ "safemem",
+ "tempfile",
+ "twoway",
+]
[[package]]
name = "native-tls"
-version = "0.2.4"
+version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d"
+checksum = "b8d96b2e1c8da3957d58100b09f102c6d9cfdfced01b7ec5a8974044bb09dbd4"
dependencies = [
"lazy_static",
"libc",
- "log 0.4.8",
+ "log",
"openssl",
"openssl-probe",
"openssl-sys",
@@ -1373,13 +2381,23 @@ dependencies = [
"tempfile",
]
+[[package]]
+name = "nb-connect"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a19900e7eee95eb2b3c2e26d12a874cc80aaf750e31be6fcbe743ead369fa45d"
+dependencies = [
+ "libc",
+ "socket2 0.4.0",
+]
+
[[package]]
name = "net2"
-version = "0.2.34"
+version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7"
+checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae"
dependencies = [
- "cfg-if",
+ "cfg-if 0.1.10",
"libc",
"winapi 0.3.9",
]
@@ -1390,11 +2408,20 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
+[[package]]
+name = "ntapi"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
+dependencies = [
+ "winapi 0.3.9",
+]
+
[[package]]
name = "num-integer"
-version = "0.1.43"
+version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b"
+checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
dependencies = [
"autocfg",
"num-traits",
@@ -1402,9 +2429,9 @@ dependencies = [
[[package]]
name = "num-traits"
-version = "0.2.12"
+version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
+checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg",
]
@@ -1420,16 +2447,21 @@ dependencies = [
]
[[package]]
-name = "object"
-version = "0.20.0"
+name = "once_cell"
+version = "1.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5"
+checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
[[package]]
-name = "once_cell"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d"
+name = "onvif-discovery-handler"
+version = "0.6.5"
+dependencies = [
+ "akri-discovery-utils 0.6.5",
+ "akri-onvif",
+ "env_logger 0.8.3",
+ "log",
+ "tokio 0.2.25",
+]
[[package]]
name = "opaque-debug"
@@ -1437,79 +2469,125 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
+[[package]]
+name = "opaque-debug"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
+
[[package]]
name = "opcua-client"
-version = "0.7.0"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb074db37fcebc35ae1280cdd87cdbda00a259f1d41da99458d1be5793d63d5d"
+checksum = "0af3d9776871cb836aa6e3f57407b3c798c7c9f46ea9fae0835f8514f89585a1"
dependencies = [
"chrono",
- "futures 0.1.29",
+ "futures 0.1.31",
"lazy_static",
- "log 0.4.8",
+ "log",
"opcua-core",
+ "opcua-crypto",
"opcua-types",
"serde",
"serde_derive",
- "time",
+ "time 0.1.44",
"tokio 0.1.22",
"tokio-codec",
"tokio-io",
"tokio-timer",
- "url 1.7.2",
]
[[package]]
name = "opcua-core"
-version = "0.7.0"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75797298070f962556f070fff8aad9746e2fa7ee5c1d0e7e6e7078f25ee42791"
+checksum = "b7dd64d0071fb9e3c1409c8ad416c3df865053209a183bbf017ee193afbda7e9"
dependencies = [
"bytes 0.4.12",
"chrono",
- "futures 0.1.29",
+ "futures 0.1.31",
"lazy_static",
- "log 0.4.8",
+ "log",
+ "opcua-crypto",
"opcua-types",
- "openssl",
- "regex 0.2.11",
+ "regex",
"serde",
"serde_derive",
"serde_yaml",
"tokio 0.1.22",
"tokio-io",
+ "url 1.7.2",
+]
+
+[[package]]
+name = "opcua-crypto"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5754e8be576bf03903a7c71b5a0a6e40a267fcc6742c83974399cdc7146362e"
+dependencies = [
+ "bytes 0.4.12",
+ "chrono",
+ "gethostname",
+ "lazy_static",
+ "log",
+ "opcua-types",
+ "openssl",
+ "serde",
+ "serde_derive",
+]
+
+[[package]]
+name = "opcua-discovery-handler"
+version = "0.6.5"
+dependencies = [
+ "akri-discovery-utils 0.6.5",
+ "akri-opcua",
+ "env_logger 0.8.3",
+ "log",
+ "tokio 0.2.25",
]
[[package]]
name = "opcua-types"
-version = "0.7.0"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7bf90304fb0ec73e8deea05aedaa2021f173911a2c8407386b7f55eeb1d6e24"
+checksum = "247dd311285d4f846219b3809c048719d3a1fd6c7b01ba16b0a6651290913d61"
dependencies = [
- "base64 0.6.0",
+ "base64 0.12.3",
"bitflags",
"byteorder",
"chrono",
"lazy_static",
- "log 0.4.8",
- "regex 0.2.11",
+ "log",
+ "regex",
+ "serde",
+ "serde_derive",
+ "uuid",
+]
+
+[[package]]
+name = "openapi"
+version = "1.1.0"
+source = "git+https://github.com/DazWilkin/openapi-admission-v1?tag=v1.1.0#60a9ba6bd64efda65cb136a21e0d6a53e962c415"
+dependencies = [
+ "reqwest 0.11.2",
"serde",
"serde_derive",
+ "serde_json",
"url 1.7.2",
- "uuid 0.5.1",
]
[[package]]
name = "openssl"
-version = "0.10.30"
+version = "0.10.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4"
+checksum = "a61075b62a23fef5a29815de7536d940aa35ce96d18ce0cc5076272db678a577"
dependencies = [
"bitflags",
- "cfg-if",
+ "cfg-if 1.0.0",
"foreign-types",
- "lazy_static",
"libc",
+ "once_cell",
"openssl-sys",
]
@@ -1521,9 +2599,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
[[package]]
name = "openssl-sys"
-version = "0.9.58"
+version = "0.9.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de"
+checksum = "313752393519e876837e09e1fa183ddef0be7735868dced3196f4472d536277f"
dependencies = [
"autocfg",
"cc",
@@ -1534,18 +2612,24 @@ dependencies = [
[[package]]
name = "ordered-float"
-version = "1.1.0"
+version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3741934be594d77de1c8461ebcbbe866f585ea616a9753aa78f2bdc69f0e4579"
+checksum = "3305af35278dd29f46fcdd139e0b1fbfae2153f0e5928b39b035542dd31e37b7"
dependencies = [
"num-traits",
]
+[[package]]
+name = "os_str_bytes"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "afb2e1c3ee07430c2cf76151675e583e0f19985fa6efae47d6848a3e2c824f85"
+
[[package]]
name = "parking"
-version = "1.0.4"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1efcee3c6d23b94012e240525f131c6abaa9e5eeb8f211002d93beec3b7be350"
+checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
[[package]]
name = "parking_lot"
@@ -1553,23 +2637,48 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252"
dependencies = [
- "lock_api",
- "parking_lot_core",
+ "lock_api 0.3.4",
+ "parking_lot_core 0.6.2",
"rustc_version",
]
+[[package]]
+name = "parking_lot"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
+dependencies = [
+ "instant",
+ "lock_api 0.4.3",
+ "parking_lot_core 0.8.3",
+]
+
[[package]]
name = "parking_lot_core"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b"
dependencies = [
- "cfg-if",
+ "cfg-if 0.1.10",
"cloudabi",
"libc",
- "redox_syscall",
+ "redox_syscall 0.1.57",
"rustc_version",
- "smallvec 0.6.13",
+ "smallvec 0.6.14",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
+dependencies = [
+ "cfg-if 1.0.0",
+ "instant",
+ "libc",
+ "redox_syscall 0.2.5",
+ "smallvec 1.6.1",
"winapi 0.3.9",
]
@@ -1631,7 +2740,7 @@ checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
dependencies = [
"maplit",
"pest",
- "sha-1",
+ "sha-1 0.8.2",
]
[[package]]
@@ -1646,18 +2755,38 @@ dependencies = [
[[package]]
name = "pin-project"
-version = "0.4.22"
+version = "0.4.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "918192b5c59119d51e0cd221f4d49dde9112824ba717369e903c97d076083d0f"
+dependencies = [
+ "pin-project-internal 0.4.28",
+]
+
+[[package]]
+name = "pin-project"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc174859768806e91ae575187ada95c91a29e96a98dc5d2cd9a1fed039501ba6"
+dependencies = [
+ "pin-project-internal 1.0.6",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12e3a6cdbfe94a5e4572812a0201f8c0ed98c1c452c7b8563ce2276988ef9c17"
+checksum = "3be26700300be6d9d23264c73211d8190e755b6b5ca7a1b28230025511b52a5e"
dependencies = [
- "pin-project-internal",
+ "proc-macro2",
+ "quote",
+ "syn",
]
[[package]]
name = "pin-project-internal"
-version = "0.4.22"
+version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a0ffd45cf79d88737d7cc85bfd5d2894bee1139b356e616fe85dc389c61aaf7"
+checksum = "a490329918e856ed1b083f244e3bfe2d8c4f336407e4ea9e1a9f479ff09049e5"
dependencies = [
"proc-macro2",
"quote",
@@ -1666,9 +2795,15 @@ dependencies = [
[[package]]
name = "pin-project-lite"
-version = "0.1.7"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715"
+checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905"
[[package]]
name = "pin-utils"
@@ -1678,64 +2813,149 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
-version = "0.3.18"
+version = "0.3.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
+
+[[package]]
+name = "polling"
+version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33"
+checksum = "4fc12d774e799ee9ebae13f4076ca003b40d18a11ac0f3641e6f899618580b7b"
+dependencies = [
+ "cfg-if 1.0.0",
+ "libc",
+ "log",
+ "wepoll-sys",
+ "winapi 0.3.9",
+]
[[package]]
name = "ppv-lite86"
-version = "0.2.8"
+version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
+checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "predicates"
-version = "1.0.4"
+version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "347a1b6f0b21e636bc9872fb60b83b8e185f6f5516298b8238699f7f9a531030"
+checksum = "eeb433456c1a57cc93554dea3ce40b4c19c4057e41c55d4a0f3d84ea71c325aa"
dependencies = [
"difference",
"float-cmp",
"normalize-line-endings",
"predicates-core",
- "regex 1.3.9",
+ "regex",
]
[[package]]
name = "predicates-core"
-version = "1.0.0"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57e35a3326b75e49aa85f5dc6ec15b41108cf5aee58eabb1f274dd18b73c2451"
+
+[[package]]
+name = "predicates-tree"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15f553275e5721409451eb85e15fd9a860a6e5ab4496eb215987502b5f5391f2"
+dependencies = [
+ "predicates-core",
+ "treeline",
+]
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-hack"
+version = "0.5.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
+
+[[package]]
+name = "proc-macro-nested"
+version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06075c3a3e92559ff8929e7a280684489ea27fe44805174c3ebd9328dcb37178"
+checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
[[package]]
-name = "predicates-tree"
-version = "1.0.0"
+name = "proc-macro2"
+version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e63c4859013b38a76eca2414c64911fba30def9e3202ac461a2d22831220124"
+checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
dependencies = [
- "predicates-core",
- "treeline",
+ "unicode-xid",
]
[[package]]
-name = "proc-macro-hack"
-version = "0.5.16"
+name = "procfs"
+version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4"
+checksum = "ab8809e0c18450a2db0f236d2a44ec0b4c1412d0eb936233579f0990faa5d5cd"
+dependencies = [
+ "bitflags",
+ "byteorder",
+ "flate2",
+ "hex",
+ "lazy_static",
+ "libc",
+]
[[package]]
-name = "proc-macro-nested"
-version = "0.1.6"
+name = "prometheus"
+version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a"
+checksum = "c8425533e7122f0c3cc7a37e6244b16ad3a2cc32ae7ac6276e2a75da0d9c200d"
+dependencies = [
+ "cfg-if 1.0.0",
+ "fnv",
+ "lazy_static",
+ "libc",
+ "parking_lot 0.11.1",
+ "procfs",
+ "protobuf",
+ "regex",
+ "thiserror",
+]
[[package]]
-name = "proc-macro2"
-version = "1.0.18"
+name = "prometheus"
+version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
+checksum = "5986aa8d62380092d2f50f8b1cdba9cb9b6731ffd4b25b51fd126b6c3e05b99c"
dependencies = [
- "unicode-xid",
+ "cfg-if 1.0.0",
+ "fnv",
+ "lazy_static",
+ "libc",
+ "memchr",
+ "parking_lot 0.11.1",
+ "procfs",
+ "protobuf",
+ "thiserror",
]
[[package]]
@@ -1757,7 +2977,7 @@ dependencies = [
"bytes 0.5.6",
"heck",
"itertools",
- "log 0.4.8",
+ "log",
"multimap",
"petgraph",
"prost",
@@ -1789,6 +3009,12 @@ dependencies = [
"prost",
]
+[[package]]
+name = "protobuf"
+version = "2.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b7f4a129bb3754c25a4e04032a90173c68f85168f77118ac4cb4936e7f06f92"
+
[[package]]
name = "quick-error"
version = "1.2.3"
@@ -1797,48 +3023,37 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
-version = "1.0.7"
+version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
+checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
-version = "0.3.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c"
-dependencies = [
- "libc",
- "rand 0.4.6",
-]
-
-[[package]]
-name = "rand"
-version = "0.4.6"
+version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
+checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
- "fuchsia-cprng",
+ "getrandom 0.1.16",
"libc",
- "rand_core 0.3.1",
- "rdrand",
- "winapi 0.3.9",
+ "rand_chacha 0.2.2",
+ "rand_core 0.5.1",
+ "rand_hc 0.2.0",
+ "rand_pcg",
]
[[package]]
name = "rand"
-version = "0.7.3"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
+checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
dependencies = [
- "getrandom",
"libc",
- "rand_chacha",
- "rand_core 0.5.1",
- "rand_hc",
- "rand_pcg",
+ "rand_chacha 0.3.0",
+ "rand_core 0.6.2",
+ "rand_hc 0.3.0",
]
[[package]]
@@ -1852,27 +3067,31 @@ dependencies = [
]
[[package]]
-name = "rand_core"
-version = "0.3.1"
+name = "rand_chacha"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
+checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
dependencies = [
- "rand_core 0.4.2",
+ "ppv-lite86",
+ "rand_core 0.6.2",
]
[[package]]
name = "rand_core"
-version = "0.4.2"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
+checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+dependencies = [
+ "getrandom 0.1.16",
+]
[[package]]
name = "rand_core"
-version = "0.5.1"
+version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
dependencies = [
- "getrandom",
+ "getrandom 0.2.2",
]
[[package]]
@@ -1885,21 +3104,21 @@ dependencies = [
]
[[package]]
-name = "rand_pcg"
-version = "0.2.1"
+name = "rand_hc"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
+checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
dependencies = [
- "rand_core 0.5.1",
+ "rand_core 0.6.2",
]
[[package]]
-name = "rdrand"
-version = "0.4.0"
+name = "rand_pcg"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
+checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
dependencies = [
- "rand_core 0.3.1",
+ "rand_core 0.5.1",
]
[[package]]
@@ -1909,64 +3128,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]]
-name = "redox_users"
-version = "0.3.4"
+name = "redox_syscall"
+version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431"
+checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
dependencies = [
- "getrandom",
- "redox_syscall",
- "rust-argon2",
+ "bitflags",
]
[[package]]
-name = "regex"
-version = "0.2.11"
+name = "redox_users"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384"
+checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
dependencies = [
- "aho-corasick 0.6.10",
- "memchr",
- "regex-syntax 0.5.6",
- "thread_local 0.3.6",
- "utf8-ranges",
+ "getrandom 0.1.16",
+ "redox_syscall 0.1.57",
+ "rust-argon2",
]
[[package]]
name = "regex"
-version = "1.3.9"
+version = "1.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6"
+checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19"
dependencies = [
- "aho-corasick 0.7.13",
+ "aho-corasick",
"memchr",
- "regex-syntax 0.6.18",
- "thread_local 1.0.1",
-]
-
-[[package]]
-name = "regex-syntax"
-version = "0.5.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7"
-dependencies = [
- "ucd-util",
+ "regex-syntax",
]
[[package]]
name = "regex-syntax"
-version = "0.6.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
-
-[[package]]
-name = "relay"
-version = "0.1.1"
+version = "0.6.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a"
-dependencies = [
- "futures 0.1.29",
-]
+checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
[[package]]
name = "remove_dir_all"
@@ -1979,38 +3175,97 @@ dependencies = [
[[package]]
name = "reqwest"
-version = "0.10.6"
+version = "0.10.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b82c9238b305f26f53443e3a4bc8528d64b8d0bee408ec949eb7bf5635ec680"
+checksum = "0718f81a8e14c4dbb3b34cf23dc6aaf9ab8a0dfec160c534b3dbca1aaa21f47c"
dependencies = [
"async-compression",
- "base64 0.12.3",
+ "base64 0.13.0",
"bytes 0.5.6",
"encoding_rs",
"futures-core",
"futures-util",
- "http 0.2.1",
- "http-body",
- "hyper 0.13.7",
+ "http 0.2.3",
+ "http-body 0.3.1",
+ "hyper 0.13.10",
"hyper-tls",
+ "ipnet",
"js-sys",
"lazy_static",
- "log 0.4.8",
+ "log",
"mime",
"mime_guess",
"native-tls",
"percent-encoding 2.1.0",
- "pin-project-lite",
+ "pin-project-lite 0.2.6",
"serde",
"serde_json",
- "serde_urlencoded",
- "tokio 0.2.21",
+ "serde_urlencoded 0.7.0",
+ "tokio 0.2.25",
"tokio-tls",
- "url 2.1.1",
+ "url 2.2.1",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "winreg 0.7.0",
+]
+
+[[package]]
+name = "reqwest"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf12057f289428dbf5c591c74bf10392e4a8003f993405a902f20117019022d4"
+dependencies = [
+ "base64 0.13.0",
+ "bytes 1.0.1",
+ "encoding_rs",
+ "futures-core",
+ "futures-util",
+ "http 0.2.3",
+ "http-body 0.4.1",
+ "hyper 0.14.7",
+ "ipnet",
+ "js-sys",
+ "lazy_static",
+ "log",
+ "mime",
+ "mime_guess",
+ "percent-encoding 2.1.0",
+ "pin-project-lite 0.2.6",
+ "serde",
+ "serde_json",
+ "serde_urlencoded 0.7.0",
+ "tokio 1.4.0",
+ "url 2.2.1",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
- "winreg",
+ "winreg 0.7.0",
+]
+
+[[package]]
+name = "resolv-conf"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00"
+dependencies = [
+ "hostname",
+ "quick-error",
+]
+
+[[package]]
+name = "ring"
+version = "0.16.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
+dependencies = [
+ "cc",
+ "libc",
+ "once_cell",
+ "spin",
+ "untrusted",
+ "web-sys",
+ "winapi 0.3.9",
]
[[package]]
@@ -2024,22 +3279,16 @@ dependencies = [
[[package]]
name = "rust-argon2"
-version = "0.7.0"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017"
+checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb"
dependencies = [
- "base64 0.11.0",
+ "base64 0.13.0",
"blake2b_simd",
"constant_time_eq",
- "crossbeam-utils",
+ "crossbeam-utils 0.8.3",
]
-[[package]]
-name = "rustc-demangle"
-version = "0.1.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
-
[[package]]
name = "rustc_version"
version = "0.2.3"
@@ -2050,16 +3299,36 @@ dependencies = [
]
[[package]]
-name = "ryu"
-version = "1.0.5"
+name = "rustls"
+version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
+checksum = "b25a18b1bf7387f0145e7f8324e700805aade3842dd3db2e74e4cdeb4677c09e"
+dependencies = [
+ "base64 0.10.1",
+ "log",
+ "ring",
+ "sct",
+ "webpki",
+]
[[package]]
-name = "safemem"
-version = "0.2.0"
+name = "rustls"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d1126dcf58e93cee7d098dbda643b5f92ed724f1f6a63007c1116eed6700c81"
+dependencies = [
+ "base64 0.12.3",
+ "log",
+ "ring",
+ "sct",
+ "webpki",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f"
+checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "safemem"
@@ -2095,11 +3364,21 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+[[package]]
+name = "sct"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
[[package]]
name = "security-framework"
-version = "0.4.4"
+version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535"
+checksum = "3670b1d2fdf6084d192bc71ead7aabe6c06aa2ea3fbd9cc3ac111fa5c2b1bd84"
dependencies = [
"bitflags",
"core-foundation",
@@ -2110,9 +3389,9 @@ dependencies = [
[[package]]
name = "security-framework-sys"
-version = "0.4.3"
+version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17bf11d99252f512695eb468de5516e5cf75455521e69dfe343f3b74e4748405"
+checksum = "3676258fd3cfe2c9a0ec99ce3038798d847ce3e4bb17746373eb9f0f1ac16339"
dependencies = [
"core-foundation-sys",
"libc",
@@ -2135,9 +3414,12 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
-version = "1.0.114"
+version = "1.0.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3"
+checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
+dependencies = [
+ "serde_derive",
+]
[[package]]
name = "serde-value"
@@ -2151,9 +3433,9 @@ dependencies = [
[[package]]
name = "serde_derive"
-version = "1.0.114"
+version = "1.0.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e"
+checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
dependencies = [
"proc-macro2",
"quote",
@@ -2162,9 +3444,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.56"
+version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3433e879a558dde8b5e8feb2a04899cf34fdde1fafb894687e52105fc1162ac3"
+checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
dependencies = [
"itoa",
"ryu",
@@ -2180,14 +3462,26 @@ dependencies = [
"dtoa",
"itoa",
"serde",
- "url 2.1.1",
+ "url 2.2.1",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
]
[[package]]
name = "serde_yaml"
-version = "0.8.13"
+version = "0.8.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae3e2dd40a7cdc18ca80db804b7f461a39bb721160a85c9a1fa30134bf3c02a5"
+checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23"
dependencies = [
"dtoa",
"linked-hash-map",
@@ -2201,27 +3495,39 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
dependencies = [
- "block-buffer",
- "digest",
+ "block-buffer 0.7.3",
+ "digest 0.8.1",
"fake-simd",
- "opaque-debug",
+ "opaque-debug 0.2.3",
]
[[package]]
-name = "signal-hook-registry"
-version = "1.2.0"
+name = "sha-1"
+version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41"
+checksum = "dfebf75d25bd900fd1e7d11501efab59bc846dbc76196839663e6637bba9f25f"
dependencies = [
- "arc-swap",
- "libc",
+ "block-buffer 0.9.0",
+ "cfg-if 1.0.0",
+ "cpuid-bool",
+ "digest 0.9.0",
+ "opaque-debug 0.3.0",
]
[[package]]
-name = "slab"
-version = "0.3.0"
+name = "sha1"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
+checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6"
+dependencies = [
+ "libc",
+]
[[package]]
name = "slab"
@@ -2231,57 +3537,115 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
[[package]]
name = "smallvec"
-version = "0.2.1"
+version = "0.6.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013"
+checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0"
+dependencies = [
+ "maybe-uninit",
+]
[[package]]
name = "smallvec"
-version = "0.6.13"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
+
+[[package]]
+name = "socket2"
+version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6"
+checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e"
dependencies = [
- "maybe-uninit",
+ "cfg-if 1.0.0",
+ "libc",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "socket2"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2"
+dependencies = [
+ "libc",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "spin"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+
+[[package]]
+name = "standback"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "stdweb"
+version = "0.4.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5"
+dependencies = [
+ "discard",
+ "rustc_version",
+ "stdweb-derive",
+ "stdweb-internal-macros",
+ "stdweb-internal-runtime",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "stdweb-derive"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "serde",
+ "serde_derive",
+ "syn",
+]
+
+[[package]]
+name = "stdweb-internal-macros"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11"
+dependencies = [
+ "base-x",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "sha1",
+ "syn",
]
[[package]]
-name = "smol"
-version = "0.1.18"
+name = "stdweb-internal-runtime"
+version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "620cbb3c6e34da57d3a248cda0cd01cd5848164dc062e764e65d06fe3ea7aed5"
-dependencies = [
- "async-task",
- "blocking",
- "concurrent-queue",
- "fastrand",
- "futures-io",
- "futures-util",
- "libc",
- "once_cell",
- "scoped-tls 1.0.0",
- "slab 0.4.2",
- "socket2",
- "wepoll-sys-stjepang",
- "winapi 0.3.9",
-]
+checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
[[package]]
-name = "socket2"
-version = "0.3.12"
+name = "strsim"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918"
-dependencies = [
- "cfg-if",
- "libc",
- "redox_syscall",
- "winapi 0.3.9",
-]
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "subtle"
-version = "1.0.0"
+version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
+checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2"
[[package]]
name = "sxd-document"
@@ -2306,70 +3670,61 @@ dependencies = [
[[package]]
name = "syn"
-version = "1.0.34"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "936cae2873c940d92e697597c5eee105fb570cd5689c695806f672883653349b"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-xid",
-]
-
-[[package]]
-name = "synstructure"
-version = "0.12.4"
+version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
+checksum = "3ce15dd3ed8aa2f8eeac4716d6ef5ab58b6b9256db41d7e1a0224c2788e8fd87"
dependencies = [
"proc-macro2",
"quote",
- "syn",
"unicode-xid",
]
-[[package]]
-name = "take"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5"
-
[[package]]
name = "tempfile"
-version = "3.1.0"
+version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
+checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"libc",
- "rand 0.7.3",
- "redox_syscall",
+ "rand 0.8.3",
+ "redox_syscall 0.2.5",
"remove_dir_all",
"winapi 0.3.9",
]
[[package]]
name = "termcolor"
-version = "1.1.0"
+version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
+checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
dependencies = [
"winapi-util",
]
+[[package]]
+name = "textwrap"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "203008d98caf094106cfaba70acfed15e18ed3ddb7d94e49baec153a2b462789"
+dependencies = [
+ "unicode-width",
+]
+
[[package]]
name = "thiserror"
-version = "1.0.20"
+version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08"
+checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.20"
+version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793"
+checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0"
dependencies = [
"proc-macro2",
"quote",
@@ -2377,38 +3732,77 @@ dependencies = [
]
[[package]]
-name = "thread_local"
-version = "0.3.6"
+name = "threadpool"
+version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
+checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
dependencies = [
- "lazy_static",
+ "num_cpus",
]
[[package]]
-name = "thread_local"
-version = "1.0.1"
+name = "time"
+version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
+checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
dependencies = [
- "lazy_static",
+ "libc",
+ "wasi 0.10.0+wasi-snapshot-preview1",
+ "winapi 0.3.9",
]
[[package]]
name = "time"
-version = "0.1.43"
+version = "0.2.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
+checksum = "08a8cbfbf47955132d0202d1662f49b2423ae35862aee471f3ba4b133358f372"
dependencies = [
+ "const_fn",
"libc",
+ "standback",
+ "stdweb",
+ "time-macros",
+ "version_check",
"winapi 0.3.9",
]
+[[package]]
+name = "time-macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1"
+dependencies = [
+ "proc-macro-hack",
+ "time-macros-impl",
+]
+
+[[package]]
+name = "time-macros-impl"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5c3be1edfad6027c69f5491cf4cb310d1a71ecd6af742788c6ff8bced86b8fa"
+dependencies = [
+ "proc-macro-hack",
+ "proc-macro2",
+ "quote",
+ "standback",
+ "syn",
+]
+
[[package]]
name = "tinyvec"
-version = "0.3.3"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53953d2d3a5ad81d9f844a32f14ebb121f50b650cd59d0ee2a07cf13c617efed"
+checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
@@ -2417,8 +3811,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6"
dependencies = [
"bytes 0.4.12",
- "futures 0.1.29",
- "mio",
+ "futures 0.1.31",
+ "mio 0.6.23",
"num_cpus",
"tokio-codec",
"tokio-current-thread",
@@ -2436,9 +3830,9 @@ dependencies = [
[[package]]
name = "tokio"
-version = "0.2.21"
+version = "0.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d099fa27b9702bed751524694adbe393e18b36b204da91eb1cbbbbb4a5ee2d58"
+checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092"
dependencies = [
"bytes 0.5.6",
"fnv",
@@ -2447,17 +3841,31 @@ dependencies = [
"lazy_static",
"libc",
"memchr",
- "mio",
+ "mio 0.6.23",
"mio-named-pipes",
"mio-uds",
"num_cpus",
- "pin-project-lite",
+ "pin-project-lite 0.1.12",
"signal-hook-registry",
- "slab 0.4.2",
+ "slab",
"tokio-macros",
"winapi 0.3.9",
]
+[[package]]
+name = "tokio"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "134af885d758d645f0f0505c9a8b3f9bf8a348fd822e112ab5248138348f1722"
+dependencies = [
+ "autocfg",
+ "bytes 1.0.1",
+ "libc",
+ "memchr",
+ "mio 0.7.11",
+ "pin-project-lite 0.2.6",
+]
+
[[package]]
name = "tokio-codec"
version = "0.1.2"
@@ -2465,21 +3873,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b"
dependencies = [
"bytes 0.4.12",
- "futures 0.1.29",
+ "futures 0.1.31",
"tokio-io",
]
[[package]]
name = "tokio-core"
-version = "0.1.17"
+version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aeeffbbb94209023feaef3c196a41cbcdafa06b4a6f893f68779bb5e53796f71"
+checksum = "87b1395334443abca552f63d4f61d0486f12377c2ba8b368e523f89e828cffd4"
dependencies = [
"bytes 0.4.12",
- "futures 0.1.29",
+ "futures 0.1.31",
"iovec",
- "log 0.4.8",
- "mio",
+ "log",
+ "mio 0.6.23",
"scoped-tls 0.1.2",
"tokio 0.1.22",
"tokio-executor",
@@ -2494,7 +3902,7 @@ version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e"
dependencies = [
- "futures 0.1.29",
+ "futures 0.1.31",
"tokio-executor",
]
@@ -2504,8 +3912,8 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671"
dependencies = [
- "crossbeam-utils",
- "futures 0.1.29",
+ "crossbeam-utils 0.7.2",
+ "futures 0.1.31",
]
[[package]]
@@ -2514,7 +3922,7 @@ version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4"
dependencies = [
- "futures 0.1.29",
+ "futures 0.1.31",
"tokio-io",
"tokio-threadpool",
]
@@ -2526,15 +3934,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674"
dependencies = [
"bytes 0.4.12",
- "futures 0.1.29",
- "log 0.4.8",
+ "futures 0.1.31",
+ "log",
]
[[package]]
name = "tokio-macros"
-version = "0.2.5"
+version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389"
+checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a"
dependencies = [
"proc-macro2",
"quote",
@@ -2542,21 +3950,13 @@ dependencies = [
]
[[package]]
-name = "tokio-proto"
-version = "0.1.1"
+name = "tokio-openssl"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389"
+checksum = "3c4b08c5f4208e699ede3df2520aca2e82401b2de33f45e96696a074480be594"
dependencies = [
- "futures 0.1.29",
- "log 0.3.9",
- "net2",
- "rand 0.3.23",
- "slab 0.3.0",
- "smallvec 0.2.1",
- "take",
- "tokio-core",
- "tokio-io",
- "tokio-service",
+ "openssl",
+ "tokio 0.2.25",
]
[[package]]
@@ -2565,26 +3965,29 @@ version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351"
dependencies = [
- "crossbeam-utils",
- "futures 0.1.29",
+ "crossbeam-utils 0.7.2",
+ "futures 0.1.31",
"lazy_static",
- "log 0.4.8",
- "mio",
+ "log",
+ "mio 0.6.23",
"num_cpus",
- "parking_lot",
- "slab 0.4.2",
+ "parking_lot 0.9.0",
+ "slab",
"tokio-executor",
"tokio-io",
"tokio-sync",
]
[[package]]
-name = "tokio-service"
-version = "0.1.0"
+name = "tokio-rustls"
+version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162"
+checksum = "3068d891551949b37681724d6b73666787cc63fa8e255c812a41d2513aff9775"
dependencies = [
- "futures 0.1.29",
+ "futures-core",
+ "rustls 0.16.0",
+ "tokio 0.2.25",
+ "webpki",
]
[[package]]
@@ -2593,9 +3996,9 @@ version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0c34c6e548f101053321cba3da7cbb87a610b85555884c41b07da2eb91aff12"
dependencies = [
- "futures 0.1.29",
+ "futures 0.1.31",
"libc",
- "mio",
+ "mio 0.6.23",
"mio-uds",
"signal-hook-registry",
"tokio-executor",
@@ -2611,7 +4014,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee"
dependencies = [
"fnv",
- "futures 0.1.29",
+ "futures 0.1.31",
]
[[package]]
@@ -2621,9 +4024,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72"
dependencies = [
"bytes 0.4.12",
- "futures 0.1.29",
+ "futures 0.1.31",
"iovec",
- "mio",
+ "mio 0.6.23",
"tokio-io",
"tokio-reactor",
]
@@ -2636,12 +4039,12 @@ checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89"
dependencies = [
"crossbeam-deque",
"crossbeam-queue",
- "crossbeam-utils",
- "futures 0.1.29",
+ "crossbeam-utils 0.7.2",
+ "futures 0.1.31",
"lazy_static",
- "log 0.4.8",
+ "log",
"num_cpus",
- "slab 0.4.2",
+ "slab",
"tokio-executor",
]
@@ -2651,9 +4054,9 @@ version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296"
dependencies = [
- "crossbeam-utils",
- "futures 0.1.29",
- "slab 0.4.2",
+ "crossbeam-utils 0.7.2",
+ "futures 0.1.31",
+ "slab",
"tokio-executor",
]
@@ -2664,7 +4067,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a70f4fcd7b3b24fb194f837560168208f669ca8cb70d0c4b862944452396343"
dependencies = [
"native-tls",
- "tokio 0.2.21",
+ "tokio 0.2.25",
+]
+
+[[package]]
+name = "tokio-tungstenite"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d9e878ad426ca286e4dcae09cbd4e1973a7f8987d97570e2469703dd7f5720c"
+dependencies = [
+ "futures-util",
+ "log",
+ "pin-project 0.4.28",
+ "tokio 0.2.25",
+ "tungstenite",
]
[[package]]
@@ -2674,9 +4090,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82"
dependencies = [
"bytes 0.4.12",
- "futures 0.1.29",
- "log 0.4.8",
- "mio",
+ "futures 0.1.31",
+ "log",
+ "mio 0.6.23",
"tokio-codec",
"tokio-io",
"tokio-reactor",
@@ -2689,11 +4105,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab57a4ac4111c8c9dbcf70779f6fc8bc35ae4b2454809febac840ad19bd7e4e0"
dependencies = [
"bytes 0.4.12",
- "futures 0.1.29",
+ "futures 0.1.31",
"iovec",
"libc",
- "log 0.4.8",
- "mio",
+ "log",
+ "mio 0.6.23",
"mio-uds",
"tokio-codec",
"tokio-io",
@@ -2709,9 +4125,9 @@ dependencies = [
"bytes 0.5.6",
"futures-core",
"futures-sink",
- "log 0.4.8",
- "pin-project-lite",
- "tokio 0.2.21",
+ "log",
+ "pin-project-lite 0.1.12",
+ "tokio 0.2.25",
]
[[package]]
@@ -2722,10 +4138,25 @@ checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499"
dependencies = [
"bytes 0.5.6",
"futures-core",
+ "futures-io",
+ "futures-sink",
+ "log",
+ "pin-project-lite 0.1.12",
+ "tokio 0.2.25",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5143d049e85af7fbc36f5454d990e62c2df705b3589f123b71f441b6b59f443f"
+dependencies = [
+ "bytes 1.0.1",
+ "futures-core",
"futures-sink",
- "log 0.4.8",
- "pin-project-lite",
- "tokio 0.2.21",
+ "log",
+ "pin-project-lite 0.2.6",
+ "tokio 1.4.0",
]
[[package]]
@@ -2740,14 +4171,15 @@ dependencies = [
"bytes 0.5.6",
"futures-core",
"futures-util",
- "http 0.2.1",
- "http-body",
- "hyper 0.13.7",
+ "http 0.2.3",
+ "http-body 0.3.1",
+ "hyper 0.13.10",
"percent-encoding 1.0.1",
- "pin-project",
+ "pin-project 0.4.28",
"prost",
"prost-derive",
- "tokio 0.2.21",
+ "tokio 0.2.25",
+ "tokio-rustls",
"tokio-util 0.2.0",
"tower",
"tower-balance",
@@ -2797,10 +4229,10 @@ dependencies = [
"futures-core",
"futures-util",
"indexmap",
- "pin-project",
+ "pin-project 0.4.28",
"rand 0.7.3",
- "slab 0.4.2",
- "tokio 0.2.21",
+ "slab",
+ "tokio 0.2.25",
"tower-discover",
"tower-layer",
"tower-load",
@@ -2817,8 +4249,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4887dc2a65d464c8b9b66e0e4d51c2fd6cf5b3373afc72805b0a60bce00446a"
dependencies = [
"futures-core",
- "pin-project",
- "tokio 0.2.21",
+ "pin-project 0.4.28",
+ "tokio 0.2.25",
"tower-layer",
"tower-service",
"tracing",
@@ -2831,15 +4263,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f6b5000c3c54d269cc695dff28136bb33d08cbf1df2c48129e143ab65bf3c2a"
dependencies = [
"futures-core",
- "pin-project",
+ "pin-project 0.4.28",
"tower-service",
]
[[package]]
name = "tower-layer"
-version = "0.3.0"
+version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a35d656f2638b288b33495d1053ea74c40dc05ec0b92084dd71ca5566c4ed1dc"
+checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62"
[[package]]
name = "tower-limit"
@@ -2848,8 +4280,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92c3040c5dbed68abffaa0d4517ac1a454cd741044f33ab0eefab6b8d1361404"
dependencies = [
"futures-core",
- "pin-project",
- "tokio 0.2.21",
+ "pin-project 0.4.28",
+ "tokio 0.2.25",
"tower-layer",
"tower-load",
"tower-service",
@@ -2862,9 +4294,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cc79fc3afd07492b7966d7efa7c6c50f8ed58d768a6075dd7ae6591c5d2017b"
dependencies = [
"futures-core",
- "log 0.4.8",
- "pin-project",
- "tokio 0.2.21",
+ "log",
+ "pin-project 0.4.28",
+ "tokio 0.2.25",
"tower-discover",
"tower-service",
]
@@ -2876,7 +4308,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f021e23900173dc315feb4b6922510dae3e79c689b74c089112066c11f0ae4e"
dependencies = [
"futures-core",
- "pin-project",
+ "pin-project 0.4.28",
"tower-layer",
"tower-service",
]
@@ -2887,7 +4319,7 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce50370d644a0364bf4877ffd4f76404156a248d104e2cc234cd391ea5cdc965"
dependencies = [
- "tokio 0.2.21",
+ "tokio 0.2.25",
"tower-service",
]
@@ -2900,8 +4332,8 @@ dependencies = [
"futures-core",
"futures-util",
"indexmap",
- "log 0.4.8",
- "tokio 0.2.21",
+ "log",
+ "tokio 0.2.25",
"tower-service",
]
@@ -2912,17 +4344,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6727956aaa2f8957d4d9232b308fe8e4e65d99db30f42b225646e86c9b6a952"
dependencies = [
"futures-core",
- "pin-project",
- "tokio 0.2.21",
+ "pin-project 0.4.28",
+ "tokio 0.2.25",
"tower-layer",
"tower-service",
]
[[package]]
name = "tower-service"
-version = "0.3.0"
+version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860"
+checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6"
[[package]]
name = "tower-timeout"
@@ -2930,8 +4362,8 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "127b8924b357be938823eaaec0608c482d40add25609481027b96198b2e4b31e"
dependencies = [
- "pin-project",
- "tokio 0.2.21",
+ "pin-project 0.4.28",
+ "tokio 0.2.25",
"tower-layer",
"tower-service",
]
@@ -2944,27 +4376,28 @@ checksum = "d1093c19826d33807c72511e68f73b4a0469a3f22c2bd5f7d5212178b4b89674"
dependencies = [
"futures-core",
"futures-util",
- "pin-project",
+ "pin-project 0.4.28",
"tower-service",
]
[[package]]
name = "tracing"
-version = "0.1.16"
+version = "0.1.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2e2a2de6b0d5cbb13fc21193a2296888eaab62b6044479aafb3c54c01c29fcd"
+checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f"
dependencies = [
- "cfg-if",
- "log 0.4.8",
+ "cfg-if 1.0.0",
+ "log",
+ "pin-project-lite 0.2.6",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
-version = "0.1.9"
+version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0693bf8d6f2bf22c690fc61a9d21ac69efdbb894a17ed596b9af0f01e64b84b"
+checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2"
dependencies = [
"proc-macro2",
"quote",
@@ -2973,20 +4406,20 @@ dependencies = [
[[package]]
name = "tracing-core"
-version = "0.1.11"
+version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94ae75f0d28ae10786f3b1895c55fe72e79928fd5ccdebb5438c75e93fec178f"
+checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f"
dependencies = [
"lazy_static",
]
[[package]]
name = "tracing-futures"
-version = "0.2.4"
+version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c"
+checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2"
dependencies = [
- "pin-project",
+ "pin-project 1.0.6",
"tracing",
]
@@ -2997,10 +4430,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41"
[[package]]
-name = "try-lock"
-version = "0.1.0"
+name = "trust-dns-proto"
+version = "0.19.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cad71a0c0d68ab9941d2fb6e82f8fb2e86d9945b94e1661dd0aaea2b88215a9"
+dependencies = [
+ "async-trait",
+ "cfg-if 1.0.0",
+ "enum-as-inner",
+ "futures 0.3.14",
+ "idna 0.2.2",
+ "lazy_static",
+ "log",
+ "rand 0.7.3",
+ "smallvec 1.6.1",
+ "thiserror",
+ "tokio 0.2.25",
+ "url 2.2.1",
+]
+
+[[package]]
+name = "trust-dns-resolver"
+version = "0.19.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee2aa4715743892880f70885373966c83d73ef1b0838a664ef0c76fffd35e7c2"
+checksum = "710f593b371175db53a26d0b38ed2978fafb9e9e8d3868b1acd753ea18df0ceb"
+dependencies = [
+ "cfg-if 0.1.10",
+ "futures 0.3.14",
+ "ipconfig",
+ "lazy_static",
+ "log",
+ "lru-cache",
+ "resolv-conf",
+ "smallvec 1.6.1",
+ "thiserror",
+ "tokio 0.2.25",
+ "trust-dns-proto",
+]
[[package]]
name = "try-lock"
@@ -3008,6 +4474,34 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
+[[package]]
+name = "tungstenite"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0308d80d86700c5878b9ef6321f020f29b1bb9d5ff3cab25e75e23f3a492a23"
+dependencies = [
+ "base64 0.12.3",
+ "byteorder",
+ "bytes 0.5.6",
+ "http 0.2.3",
+ "httparse",
+ "input_buffer",
+ "log",
+ "rand 0.7.3",
+ "sha-1 0.9.4",
+ "url 2.2.1",
+ "utf-8",
+]
+
+[[package]]
+name = "twoway"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1"
+dependencies = [
+ "memchr",
+]
+
[[package]]
name = "typed-arena"
version = "1.7.0"
@@ -3016,9 +4510,9 @@ checksum = "a9b2228007eba4120145f785df0f6c92ea538f5a3635a612ecf4e334c8c1446d"
[[package]]
name = "typenum"
-version = "1.12.0"
+version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
+checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
[[package]]
name = "ucd-trie"
@@ -3026,33 +4520,40 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
-[[package]]
-name = "ucd-util"
-version = "0.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c85f514e095d348c279b1e5cd76795082cf15bd59b93207832abe0b1d8fed236"
-
[[package]]
name = "udev"
-version = "0.4.0"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24953d50a3bce0f5f5a9a2766567072dc9af8096f8c40ea81815da651066bc9f"
+checksum = "048df778e99eea028c08cca7853b9b521df6948b59bb29ab8bb737c057f58e6d"
dependencies = [
"libc",
"libudev-sys",
]
+[[package]]
+name = "udev-discovery-handler"
+version = "0.6.5"
+dependencies = [
+ "akri-discovery-utils 0.6.5",
+ "akri-udev",
+ "env_logger 0.8.3",
+ "log",
+ "tokio 0.2.25",
+]
+
[[package]]
name = "udev-video-broker"
-version = "0.1.5"
+version = "0.6.5"
dependencies = [
- "akri-shared",
- "env_logger",
- "futures 0.1.29",
- "log 0.4.8",
+ "akri-shared 0.6.5",
+ "env_logger 0.8.3",
+ "futures 0.1.31",
+ "lazy_static",
+ "log",
+ "prometheus 0.12.0",
"prost",
"rscam",
- "tokio 0.2.21",
+ "tokio 0.2.25",
"tonic",
"tonic-build",
]
@@ -3068,27 +4569,33 @@ dependencies = [
[[package]]
name = "unicode-bidi"
-version = "0.3.4"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
+checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0"
dependencies = [
"matches",
]
[[package]]
name = "unicode-normalization"
-version = "0.1.13"
+version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977"
+checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-segmentation"
-version = "1.6.0"
+version = "1.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
+checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
[[package]]
name = "unicode-xid"
@@ -3096,6 +4603,12 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
+[[package]]
+name = "untrusted"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
+
[[package]]
name = "url"
version = "1.7.2"
@@ -3109,76 +4622,112 @@ dependencies = [
[[package]]
name = "url"
-version = "2.1.1"
+version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb"
+checksum = "9ccd964113622c8e9322cfac19eb1004a07e636c545f325da085d5cdde6f1f8b"
dependencies = [
- "idna 0.2.0",
+ "form_urlencoded",
+ "idna 0.2.2",
"matches",
"percent-encoding 2.1.0",
]
[[package]]
-name = "utf8-ranges"
-version = "1.0.4"
+name = "urlencoding"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9232eb53352b4442e40d7900465dfc534e8cb2dc8f18656fcb2ac16112b5593"
+
+[[package]]
+name = "utf-8"
+version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba"
+checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7"
[[package]]
name = "uuid"
-version = "0.5.1"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bcc7e3b898aa6f6c08e5295b6c89258d1331e9ac578cc992fb818759951bdc22"
+checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
dependencies = [
- "rand 0.3.23",
+ "getrandom 0.2.2",
]
[[package]]
-name = "uuid"
-version = "0.8.1"
+name = "value-bag"
+version = "1.0.0-alpha.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11"
+checksum = "6b676010e055c99033117c2343b33a40a30b91fecd6c49055ac9cd2d6c305ab1"
dependencies = [
- "rand 0.7.3",
+ "ctor",
]
[[package]]
name = "vcpkg"
-version = "0.2.10"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb"
+
+[[package]]
+name = "vec-arena"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c"
+checksum = "34b2f665b594b07095e3ac3f718e13c2197143416fae4c5706cffb7b1af8d7f1"
+
+[[package]]
+name = "vec_map"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
-version = "0.9.2"
+version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
+checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "waker-fn"
-version = "1.0.0"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9571542c2ce85ce642e6b58b3364da2fb53526360dfb7c211add4f5c23105ff7"
+checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
[[package]]
name = "want"
-version = "0.0.4"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a05d9d966753fa4b5c8db73fcab5eed4549cfe0e1e4e66911e5564a0085c35d1"
+checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
dependencies = [
- "futures 0.1.29",
- "log 0.4.8",
- "try-lock 0.1.0",
+ "log",
+ "try-lock",
]
[[package]]
-name = "want"
-version = "0.3.0"
+name = "warp"
+version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
+checksum = "f41be6df54c97904af01aa23e613d4521eed7ab23537cede692d4058f6449407"
dependencies = [
- "log 0.4.8",
- "try-lock 0.2.3",
+ "bytes 0.5.6",
+ "futures 0.3.14",
+ "headers",
+ "http 0.2.3",
+ "hyper 0.13.10",
+ "log",
+ "mime",
+ "mime_guess",
+ "multipart",
+ "pin-project 0.4.28",
+ "scoped-tls 1.0.0",
+ "serde",
+ "serde_json",
+ "serde_urlencoded 0.6.1",
+ "tokio 0.2.25",
+ "tokio-tungstenite",
+ "tower-service",
+ "tracing",
+ "tracing-futures",
+ "urlencoding",
]
[[package]]
@@ -3187,13 +4736,19 @@ version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
+[[package]]
+name = "wasi"
+version = "0.10.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
+
[[package]]
name = "wasm-bindgen"
-version = "0.2.64"
+version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a634620115e4a229108b71bde263bb4220c483b3f07f5ba514ee8d15064c4c2"
+checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"serde",
"serde_json",
"wasm-bindgen-macro",
@@ -3201,13 +4756,13 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.64"
+version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e53963b583d18a5aa3aaae4b4c1cb535218246131ba22a71f05b518098571df"
+checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae"
dependencies = [
"bumpalo",
"lazy_static",
- "log 0.4.8",
+ "log",
"proc-macro2",
"quote",
"syn",
@@ -3216,11 +4771,11 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
-version = "0.4.14"
+version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dba48d66049d2a6cc8488702e7259ab7afc9043ad0dc5448444f46f2a453b362"
+checksum = "81b8b767af23de6ac18bf2168b690bed2902743ddf0fb39252e36f9e2bfc63ea"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"js-sys",
"wasm-bindgen",
"web-sys",
@@ -3228,9 +4783,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.64"
+version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fcfd5ef6eec85623b4c6e844293d4516470d8f19cd72d0d12246017eb9060b8"
+checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -3238,9 +4793,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.64"
+version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9adff9ee0e94b926ca81b57f57f86d5545cdcb1d259e21ec9bdd95b901754c75"
+checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c"
dependencies = [
"proc-macro2",
"quote",
@@ -3251,25 +4806,53 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.64"
+version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f7b90ea6c632dd06fd765d44542e234d5e63d9bb917ecd64d79778a13bd79ae"
+checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489"
[[package]]
name = "web-sys"
-version = "0.3.41"
+version = "0.3.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "863539788676619aac1a23e2df3655e96b32b0e05eb72ca34ba045ad573c625d"
+checksum = "a905d57e488fec8861446d3393670fb50d27a262344013181c2cdf9fff5481be"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
-name = "wepoll-sys-stjepang"
-version = "1.0.6"
+name = "webhook-configuration"
+version = "0.6.5"
+dependencies = [
+ "actix",
+ "actix-rt",
+ "actix-web",
+ "akri-shared 0.6.5",
+ "clap",
+ "k8s-openapi",
+ "kube",
+ "openapi",
+ "openssl",
+ "rustls 0.18.1",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "webpki"
+version = "0.21.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
+name = "wepoll-sys"
+version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6fd319e971980166b53e17b1026812ad66c6b54063be879eb182342b55284694"
+checksum = "0fcb14dea929042224824779fbc82d9fab8d2e6d3cbc0ac404de8edf489e77ff"
dependencies = [
"cc",
]
@@ -3283,6 +4866,12 @@ dependencies = [
"libc",
]
+[[package]]
+name = "widestring"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c"
+
[[package]]
name = "winapi"
version = "0.2.8"
@@ -3326,6 +4915,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+[[package]]
+name = "winreg"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9"
+dependencies = [
+ "winapi 0.3.9",
+]
+
[[package]]
name = "winreg"
version = "0.7.0"
@@ -3353,9 +4951,9 @@ checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a"
[[package]]
name = "yaml-rust"
-version = "0.4.4"
+version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d"
+checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]
@@ -3366,7 +4964,7 @@ version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe81055b36d926da70850a7c1a9bc72eff184659da29e008ce9f2af4bfee7c89"
dependencies = [
- "log 0.4.8",
+ "log",
"xml-rs",
]
diff --git a/Cargo.toml b/Cargo.toml
index 066472964..3bc9e6347 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,7 +1,23 @@
# Patch to allow invalid authority headers provided by grpc-go / kubelet
# Issue to track: https://github.com/grpc/grpc-go/issues/2628
[patch.crates-io]
-h2 = { git = "https://github.com/kate-goldenring/h2", branch = "master"}
+h2 = { git = "https://github.com/kate-goldenring/h2", branch = "master" }
[workspace]
-members = ["shared", "controller", "agent", "samples/brokers/udev-video-broker"]
+members = [
+ "shared",
+ "agent",
+ "controller",
+ "samples/brokers/udev-video-broker",
+ "webhooks/validating/configuration",
+ "discovery-utils",
+ "discovery-handlers/debug-echo",
+ "discovery-handlers/onvif",
+ "discovery-handlers/opcua",
+ "discovery-handlers/udev",
+ "discovery-handler-modules/debug-echo-discovery-handler",
+ "discovery-handler-modules/onvif-discovery-handler",
+ "discovery-handler-modules/opcua-discovery-handler",
+ "discovery-handler-modules/udev-discovery-handler",
+ "discovery-handler-modules/http-discovery-handler",
+]
diff --git a/Cross.toml b/Cross.toml
index ec196e59d..dae228924 100644
--- a/Cross.toml
+++ b/Cross.toml
@@ -2,6 +2,7 @@
passthrough = [
"PKG_CONFIG_ALLOW_CROSS",
"RUST_LOG",
+ "CARGO_INCREMENTAL",
]
[target.x86_64-unknown-linux-gnu]
diff --git a/Makefile b/Makefile
index 1598f5afc..56ebc34d2 100644
--- a/Makefile
+++ b/Makefile
@@ -2,6 +2,27 @@ BUILD_AMD64 ?= 1
BUILD_ARM32 ?= 1
BUILD_ARM64 ?= 1
+# Specify flag to build optimized release version of rust components.
+# Set to be empty to use debug builds.
+BUILD_RELEASE_FLAG ?= 1
+
+# Space separated list of rust packages to not build such as the following to not build
+# the udev discovery handler library or module: "akri-udev udev-discovery-handler"
+PACKAGES_TO_EXCLUDE ?=
+
+# Incremental compilation causes rustc to save additional information to disk which will be
+# reused when recompiling the crate, improving re-compile times.
+# The additional information is stored in the target directory.
+# By default for cargo builds, it is enabled in debug mode and disabled in release mode.
+CARGO_INCREMENTAL ?= 0
+
+BUILD_SLIM_AGENT ?= 1
+FULL_AGENT_EXECUTABLE_NAME ?= agent-full
+# Specify which features of the Agent to build, namely which Discovery Handlers
+# should be embedded if any. The "agent-full" feature must be enabled to use the embedded
+# Discovery Handlers. IE: AGENT_FEATURES="agent-full onvif-feat opcua-feat udev-feat"
+AGENT_FEATURES ?=
+
REGISTRY ?= devcaptest.azurecr.io
UNIQUE_ID ?= $(USER)
@@ -9,8 +30,13 @@ INTERMEDIATE_DOCKERFILE_DIR ?= build/containers/intermediate
DOCKERFILE_DIR ?= build/containers
PREFIX ?= $(REGISTRY)/$(UNIQUE_ID)
-VERSION=$(shell cat version.txt)
-VERSION_LABEL=v$(VERSION)
+
+# Evaluate VERSION and TIMESTAMP immediately to avoid
+# any lazy evaluation change in the values
+VERSION := $(shell cat version.txt)
+TIMESTAMP := $(shell date +"%Y%m%d_%H%M%S")
+
+VERSION_LABEL=v$(VERSION)-$(TIMESTAMP)
LABEL_PREFIX ?= $(VERSION_LABEL)
CACHE_OPTION ?=
@@ -29,4 +55,4 @@ ARM64V8_TARGET = aarch64-unknown-linux-gnu
include build/intermediate-containers.mk
# Akri container defines
-include build/akri-containers.mk
\ No newline at end of file
+include build/akri-containers.mk
diff --git a/README.md b/README.md
index 1ae5ca7c8..870984d07 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,8 @@
-# Akri
+

+
[](https://kubernetes.slack.com/messages/akri)
-[](https://blog.rust-lang.org/2020/01/30/Rust-1.41.0.html)
-[](https://v1-16.docs.kubernetes.io/)
+[](https://blog.rust-lang.org/2021/03/25/Rust-1.51.0.html)
+[](https://kubernetes.io/)
[](https://codecov.io/gh/deislabs/akri)
[](https://github.com/deislabs/akri/actions?query=workflow%3A%22Check+Rust%22)
@@ -10,35 +11,40 @@
[](https://github.com/deislabs/akri/actions?query=workflow%3A%22Build+Agent%22)
[](https://github.com/deislabs/akri/actions?query=workflow%3A%22Test+K3s%2C+Kubernetes%2C+and+MicroK8s%22)
+
+----
Akri lets you easily expose heterogeneous leaf devices (such as IP cameras and USB devices) as resources in a Kubernetes cluster, while also supporting the exposure of embedded hardware resources such as GPUs and FPGAs. Akri continually detects nodes that have access to these devices and schedules workloads based on them.
Simply put: you name it, Akri finds it, you use it.
+
+----
## Why Akri
At the edge, there are a variety of sensors, controllers, and MCU class devices that are producing data and performing actions. For Kubernetes to be a viable edge computing solution, these heterogeneous “leaf devices” need to be easily utilized by Kubernetes clusters. However, many of these leaf devices are too small to run Kubernetes themselves. Akri is an open source project that exposes these leaf devices as resources in a Kubernetes cluster. It leverages and extends the Kubernetes [device plugin framework](https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/device-plugins/), which was created with the cloud in mind and focuses on advertising static resources such as GPUs and other system hardware. Akri took this framework and applied it to the edge, where there is a diverse set of leaf devices with unique communication protocols and intermittent availability.
-Akri is made for the edge, **handling the dynamic appearance and disappearance of leaf devices**. Akri provides an abstraction layer similar to [CNI](https://github.com/containernetworking/cni), but instead of abstracting the underlying network details, it is removing the work of finding, utilizing, and monitoring the availability of the leaf device. An operator simply has to apply a Akri Configuration to a cluster, specifying the discovery protocol (say ONVIF) and the pod that should be deployed upon discovery (say a video frame server). Then, Akri does the rest. An operator can also allow multiple nodes to utilize a leaf device, thereby **providing high availability** in the case where a node goes offline. Furthermore, Akri will automatically create a Kubernetes service for each type of leaf device (or Akri Configuration), removing the need for an application to track the state of pods or nodes.
+Akri is made for the edge, **handling the dynamic appearance and disappearance of leaf devices**. Akri provides an abstraction layer similar to [CNI](https://github.com/containernetworking/cni), but instead of abstracting the underlying network details, it is removing the work of finding, utilizing, and monitoring the availability of the leaf device. An operator simply has to apply a Akri Configuration to a cluster, specifying the Discovery Handler (say ONVIF) that should be used to discover the devices and the Pod that should be deployed upon discovery (say a video frame server). Then, Akri does the rest. An operator can also allow multiple nodes to utilize a leaf device, thereby **providing high availability** in the case where a node goes offline. Furthermore, Akri will automatically create a Kubernetes service for each type of leaf device (or Akri Configuration), removing the need for an application to track the state of pods or nodes.
-Most importantly, Akri **was built to be extensible**. We currently have ONVIF, udev, and OPC UA discovery handlers, but more can be easily added by community members like you. The more protocols Akri can support, the wider an array of leaf devices Akri can discover. We are excited to work with you to build a more connected edge.
+Most importantly, Akri **was built to be extensible**. Akri currently supports ONVIF, udev, and OPC UA Discovery Handlers, but more can be easily added by community members like you. The more protocols Akri can support, the wider an array of leaf devices Akri can discover. We are excited to work with you to build a more connected edge.
## How Akri Works
-Akri’s architecture is made up of four key components: two custom resources, a device plugin implementation, and a custom controller. The first custom resource, the Akri Configuration, is where **you name it**. This tells Akri what kind of device it should look for. At this point, **Akri finds it**! Akri's device plugin implementation looks for the device and tracks its availability using Akri's second custom resource, the Akri Instance. Having found your device, the Akri Controller helps **you use it**. It sees each Akri Instance (which represents a leaf device) and deploys a ("broker") pod that knows how to connect to the resource and utilize it.
+Akri’s architecture is made up of five key components: two custom resources, Discovery Handlers, an Agent (device plugin implementation), and a custom Controller. The first custom resource, the Akri Configuration, is where **you name it**. This tells Akri what kind of device it should look for. At this point, **Akri finds it**! Akri's Discovery Handlers look for the device and inform the Agent of discovered devices. The Agent then creates Akri's second custom resource, the Akri Instance, to track the availability and usage of the device. Having found your device, the Akri Controller helps **you use it**. It sees each Akri Instance (which represents a leaf device) and deploys a ("broker") Pod that knows how to connect to the resource and utilize it.
-
## Quick Start with a Demo
Try the [end to end demo](./docs/end-to-end-demo.md) of Akri to see Akri discover mock video cameras and a streaming app display the footage from those cameras. It includes instructions on K8s cluster setup. If you would like to perform the demo on a cluster of Raspberry Pi 4's, see the [Raspberry Pi 4 demo](./docs/end-to-end-demo-rpi4.md).
## Documentation
-- [Running Akri using our currently supported protocols](./docs/user-guide.md)
+- [User guide for deploying Akri using Helm](./docs/user-guide.md)
- [Akri architecture in depth](./docs/architecture.md)
- [How to build Akri](./docs/development.md)
-- [How to extend Akri for protocols that haven't been supported yet](./docs/extensibility.md).
-- Proposals for enhancements such as new protocol implementations can be found in the [proposals folder](./docs/proposals)
+- [How to extend Akri for protocols that haven't been supported yet](./docs/discovery-handler-development.md).
+- [How to create a broker to leverage discovered devices](./docs/broker-development.md).
+- Proposals for enhancements such as new Discovery Handler implementations can be found in the [proposals folder](./docs/proposals)
## Roadmap
-Akri was built to be extensible. We currently have ONVIF, udev, OPC UA discovery protocols, but as a community, we hope to continuously support more protocols. We have created a [protocol implementation roadmap](./docs/roadmap.md#implement-additional-protocol-handlers) in order to prioritize development of protocols. If there is a protocol you feel we should prioritize, please [create an issue](https://github.com/deislabs/akri/issues/new/choose), or better yet, contribute the implementation! We are excited to work with you to build a more connected edge.
+Akri was built to be extensible. We currently have ONVIF, udev, OPC UA Discovery Handlers, but as a community, we hope to continuously support more protocols. We have created a [Discovery Handler implementation roadmap](./docs/roadmap.md#implement-additional-discovery-handlers) in order to prioritize development of Discovery Handlers. If there is a protocol you feel we should prioritize, please [create an issue](https://github.com/deislabs/akri/issues/new/choose), or better yet, contribute the implementation!
## Contributing
This project welcomes contributions, whether by [creating new issues](https://github.com/deislabs/akri/issues/new/choose) or pull requests. See our [contributing document](./docs/contributing.md) on how to get started.
diff --git a/agent/Cargo.toml b/agent/Cargo.toml
index 599c944c0..ea7a73b5d 100644
--- a/agent/Cargo.toml
+++ b/agent/Cargo.toml
@@ -1,53 +1,68 @@
[package]
name = "agent"
-version = "0.1.5"
+version = "0.6.5"
authors = ["Kate Goldenring ", ""]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+akri-debug-echo = { path = "../discovery-handlers/debug-echo" }
+akri-discovery-utils = { path = "../discovery-utils" }
+akri-onvif = { path = "../discovery-handlers/onvif", optional = true }
+akri-opcua = { path = "../discovery-handlers/opcua", optional = true }
+akri-udev = { path = "../discovery-handlers/udev", optional = true }
+akri-shared = { path = "../shared" }
+anyhow = "1.0.38"
async-stream = "0.2"
async-trait = "0.1.0"
-blake2 = "0.8.0"
+blake2 = "0.9.0"
chrono = "0.4.10"
-cfg-if = "0.1"
-env_logger = "0.6.1"
-failure = "0.1.5"
+cfg-if = "1.0.0"
+env_logger = "0.8.3"
futures = { version = "0.3.1", package = "futures" }
futures-core = "0.3"
futures-util = "0.3"
-futures-old = { version = "0.1", package = "futures" }
-hyper = "0.11"
+hyper = "0.13.10"
+h2 = "=0.2.6"
kube = { version = "0.23.0", features = ["openapi"] }
k8s-openapi = { version = "0.6.0", features = ["v1_16"] }
+lazy_static = "1.4"
log = "0.4"
-mime = "0.3"
-mockall = "0.6.0"
-opcua-client = "0.7.0"
-pest = "2.0"
-pest_derive = "2.0"
+mockall_double = "0.2.0"
+prometheus = { version = "0.12.0", features = ["process"] }
prost = "0.6"
-rand = "0.7"
-regex = "1"
+rand = "0.8.3"
serde = "1.0.104"
serde_json = "1.0.45"
serde_yaml = "0.8.11"
serde_derive = "1.0.104"
-akri-shared = { path = "../shared" }
-sxd-document = "0.3.0"
-sxd-xpath = "0.4.0"
-tempfile = "3.1.0"
tokio = { version = "0.2", features = ["full"] }
tokio-core = "0.1"
tonic = "0.1"
tower = "0.3"
-udev = "0.4"
-url = "2.1.0"
+url = "2.2.0"
uuid = { version = "0.8.1", features = ["v4"] }
-xml-rs = "0.8.0"
-yaserde = "0.3.13"
-yaserde_derive = "0.3.13"
[build-dependencies]
tonic-build = "0.1.1"
+
+[dev-dependencies]
+# for testing using a simple discovery handler
+akri-discovery-utils = { path = "../discovery-utils", features = ["mock-discovery-handler"] }
+akri-onvif = { path = "../discovery-handlers/onvif" }
+akri-opcua = { path = "../discovery-handlers/opcua" }
+akri-udev = { path = "../discovery-handlers/udev"}
+mockall = "0.9.0"
+mock_instant = { version = "0.2", features = ["sync"] }
+tempfile = "3.1.0"
+
+[features]
+# To embed discovery handlers, add the desired discovery handler features to default and "agent-full".
+# Or set the features when building the agent with
+# `cargo build --manifest-path agent/Cargo.toml --features "agent-full onvif-feat opcua-feat udev-feat"`
+default = []
+onvif-feat = [ "akri-onvif"]
+opcua-feat = ["akri-opcua"]
+udev-feat = ["akri-udev"]
+agent-full = []
\ No newline at end of file
diff --git a/agent/README.md b/agent/README.md
index b086152a9..d9f30c3b7 100644
--- a/agent/README.md
+++ b/agent/README.md
@@ -6,12 +6,14 @@ This is the Akri Agent project. It is an implementation of a [Kubernetes device
## Traits
### Public
-* **DiscoveryHandler** - This provides an abstraction to allow protocol specific code to handle discovery and provide details for Instance creation. Planned implementations of this trait include `OnvifDiscoveryHandler`, `UdevDiscoveryHandler`, `OpcuaDiscoveryHandler`, and `DebugEchoDiscoveryHandler`.
+* **DiscoveryHandler** - This provides an abstraction to allow protocol specific code to handle discovery and provide details for Instance creation. The trait is defined by Akri's [discovery API](../discovery-utils/proto/discovery.proto). Implementations of this trait can be found in the [discovery handlers directory](../discovery-handlers).
```Rust
#[async_trait]
pub trait DiscoveryHandler {
- async fn discover(&self) -> Result, Error>;
- fn are_shared(&self) -> Result;
+ async fn discover(
+ &self,
+ request: tonic::Request,
+ ) -> Result, tonic::Status>;
}
```
diff --git a/agent/src/main.rs b/agent/src/main.rs
index 849dcefad..358f38553 100644
--- a/agent/src/main.rs
+++ b/agent/src/main.rs
@@ -1,29 +1,40 @@
+extern crate hyper;
#[macro_use]
-extern crate log;
+extern crate lazy_static;
#[macro_use]
-extern crate yaserde_derive;
+extern crate log;
#[macro_use]
extern crate serde_derive;
-
-extern crate pest;
-#[macro_use]
-extern crate pest_derive;
-
-extern crate hyper;
extern crate tokio_core;
-
-mod protocols;
mod util;
-use akri_shared::akri::API_NAMESPACE;
-use env_logger;
+use akri_shared::akri::{metrics::run_metrics_server, API_NAMESPACE};
use log::{info, trace};
-use std::time::Duration;
+use prometheus::{HistogramVec, IntGaugeVec};
+use std::{
+ collections::HashMap,
+ sync::{Arc, Mutex},
+ time::Duration,
+};
+use tokio::sync::broadcast;
+#[cfg(feature = "agent-full")]
+use util::registration::register_embedded_discovery_handlers;
use util::{
- config_action, constants::SLOT_RECONCILIATION_SLOT_GRACE_PERIOD_SECS,
+ config_action,
+ constants::{
+ NEW_DISCOVERY_HANDLER_CHANNEL_CAPACITY, SLOT_RECONCILIATION_SLOT_GRACE_PERIOD_SECS,
+ },
+ registration::{run_registration_server, DiscoveryHandlerName},
slot_reconciliation::periodic_slot_reconciliation,
};
+lazy_static! {
+ // Reports the number of Instances visible to this node, grouped by Configuration and whether it is shared
+ pub static ref INSTANCE_COUNT_METRIC: IntGaugeVec = prometheus::register_int_gauge_vec!("akri_instance_count", "Akri Instance Count", &["configuration", "is_shared"]).unwrap();
+ // Reports the time to get discovery results, grouped by Configuration
+ pub static ref DISCOVERY_RESPONSE_TIME_METRIC: HistogramVec = prometheus::register_histogram_vec!("akri_discovery_response_time", "Akri Discovery Response Time", &["configuration"]).unwrap();
+}
+
/// This is the entry point for the Akri Agent.
/// It must be built on unix systems, since the underlying libraries for the `DevicePluginService` unix socket connection are unix only.
#[cfg(unix)]
@@ -43,6 +54,28 @@ async fn main() -> Result<(), Box
let mut tasks = Vec::new();
+ // Start server for Prometheus metrics
+ tasks.push(tokio::spawn(async move {
+ run_metrics_server().await.unwrap();
+ }));
+
+ let discovery_handler_map = Arc::new(Mutex::new(HashMap::new()));
+ let discovery_handler_map_clone = discovery_handler_map.clone();
+ let (new_discovery_handler_sender, _): (
+ broadcast::Sender,
+ broadcast::Receiver,
+ ) = broadcast::channel(NEW_DISCOVERY_HANDLER_CHANNEL_CAPACITY);
+ let new_discovery_handler_sender_clone = new_discovery_handler_sender.clone();
+ #[cfg(feature = "agent-full")]
+ register_embedded_discovery_handlers(discovery_handler_map_clone.clone())?;
+
+ // Start registration service for registering `DiscoveryHandlers`
+ tasks.push(tokio::spawn(async move {
+ run_registration_server(discovery_handler_map_clone, new_discovery_handler_sender)
+ .await
+ .unwrap();
+ }));
+
tasks.push(tokio::spawn(async move {
let slot_grace_period = Duration::from_secs(SLOT_RECONCILIATION_SLOT_GRACE_PERIOD_SECS);
periodic_slot_reconciliation(slot_grace_period)
@@ -51,7 +84,9 @@ async fn main() -> Result<(), Box
}));
tasks.push(tokio::spawn(async move {
- config_action::do_config_watch().await.unwrap()
+ config_action::do_config_watch(discovery_handler_map, new_discovery_handler_sender_clone)
+ .await
+ .unwrap()
}));
futures::future::try_join_all(tasks).await?;
diff --git a/agent/src/protocols/debug_echo/discovery_handler.rs b/agent/src/protocols/debug_echo/discovery_handler.rs
deleted file mode 100644
index 017e100ad..000000000
--- a/agent/src/protocols/debug_echo/discovery_handler.rs
+++ /dev/null
@@ -1,59 +0,0 @@
-use super::super::{DiscoveryHandler, DiscoveryResult};
-use akri_shared::akri::configuration::DebugEchoDiscoveryHandlerConfig;
-use async_trait::async_trait;
-use failure::Error;
-use std::{collections::HashMap, fs};
-
-/// File acting as an environment variable for testing discovery.
-/// To mimic an instance going offline, kubectl exec into one of the akri-agent-daemonset pods
-/// and echo "OFFLINE" > /tmp/debug-echo-availability.txt
-/// To mimic a device coming back online, remove the word "OFFLINE" from the file
-/// ie: echo "" > /tmp/debug-echo-availability.txt
-pub const DEBUG_ECHO_AVAILABILITY_CHECK_PATH: &str = "/tmp/debug-echo-availability.txt";
-/// String to write into DEBUG_ECHO_AVAILABILITY_CHECK_PATH to make DebugEcho devices undiscoverable
-pub const OFFLINE: &str = "OFFLINE";
-
-/// `DebugEchoDiscoveryHandler` contains a `DebugEchoDiscoveryHandlerConfig` which has a
-/// list of mock instances (`discovery_handler_config.descriptions`) and their sharability.
-/// It mocks discovering the instances by inspecting the contents of the file at `DEBUG_ECHO_AVAILABILITY_CHECK_PATH`.
-/// If the file contains "OFFLINE", it won't discover any of the instances, else it discovers them all.
-#[derive(Debug)]
-pub struct DebugEchoDiscoveryHandler {
- discovery_handler_config: DebugEchoDiscoveryHandlerConfig,
-}
-
-impl DebugEchoDiscoveryHandler {
- pub fn new(discovery_handler_config: &DebugEchoDiscoveryHandlerConfig) -> Self {
- DebugEchoDiscoveryHandler {
- discovery_handler_config: discovery_handler_config.clone(),
- }
- }
-}
-
-#[async_trait]
-impl DiscoveryHandler for DebugEchoDiscoveryHandler {
- async fn discover(&self) -> Result, Error> {
- let availability =
- fs::read_to_string(DEBUG_ECHO_AVAILABILITY_CHECK_PATH).unwrap_or_default();
- trace!(
- "discover -- DebugEcho capabilities visible? {}",
- !availability.contains(OFFLINE)
- );
- // If the device is offline, return an empty list of instance info
- if availability.contains(OFFLINE) {
- Ok(Vec::new())
- } else {
- Ok(self
- .discovery_handler_config
- .descriptions
- .iter()
- .map(|description| {
- DiscoveryResult::new(description, HashMap::new(), self.are_shared().unwrap())
- })
- .collect::>())
- }
- }
- fn are_shared(&self) -> Result {
- Ok(self.discovery_handler_config.shared)
- }
-}
diff --git a/agent/src/protocols/debug_echo/mod.rs b/agent/src/protocols/debug_echo/mod.rs
deleted file mode 100644
index edff99faf..000000000
--- a/agent/src/protocols/debug_echo/mod.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-mod discovery_handler;
-pub use self::discovery_handler::{
- DebugEchoDiscoveryHandler, DEBUG_ECHO_AVAILABILITY_CHECK_PATH, OFFLINE,
-};
diff --git a/agent/src/protocols/mod.rs b/agent/src/protocols/mod.rs
deleted file mode 100644
index 17db42d72..000000000
--- a/agent/src/protocols/mod.rs
+++ /dev/null
@@ -1,201 +0,0 @@
-use akri_shared::{
- akri::configuration::ProtocolHandler,
- os::env_var::{ActualEnvVarQuery, EnvVarQuery},
-};
-use async_trait::async_trait;
-use blake2::digest::{Input, VariableOutput};
-use blake2::VarBlake2b;
-use failure::Error;
-use std::collections::HashMap;
-
-#[derive(Debug, Clone, PartialEq)]
-pub struct DiscoveryResult {
- pub digest: String,
- pub properties: HashMap,
-}
-impl DiscoveryResult {
- fn new(id_to_digest: &str, properties: HashMap, shared: bool) -> Self {
- let mut id_to_digest = id_to_digest.to_string();
- // For unshared devices, include node hostname in id_to_digest so instances have unique names
- if !shared {
- id_to_digest = format!(
- "{}{}",
- &id_to_digest,
- std::env::var("AGENT_NODE_NAME").unwrap()
- );
- }
- let mut hasher = VarBlake2b::new(3).unwrap();
- hasher.input(id_to_digest);
- let digest = hasher
- .vec_result()
- .iter()
- .map(|num| format!("{:02x}", num))
- .collect::>()
- .join("");
- DiscoveryResult { digest, properties }
- }
-}
-
-/// DiscoveryHandler describes anything that can find available instances and define
-/// whether they are shared.
-///
-/// DiscoveryHandler provides an abstraction to help in Instance
-/// creation: search/find for instances, specify whether the instance
-/// should be shared, etc.
-///
-/// # Examples
-///
-/// ```
-/// pub struct SampleDiscoveryHandler {}
-/// #[async_trait]
-/// impl DiscoveryHandler for SampleDiscoveryHandler {
-/// async fn discover(&self) -> Result, failure::Error> {
-/// Ok(Vec::new())
-/// }
-/// fn are_shared(&self) -> Result {
-/// Ok(true)
-/// }
-/// }
-/// ```
-#[async_trait]
-pub trait DiscoveryHandler {
- async fn discover(&self) -> Result, Error>;
- fn are_shared(&self) -> Result;
-}
-
-pub mod debug_echo;
-mod onvif;
-mod opcua;
-mod udev;
-
-pub fn get_discovery_handler(
- discovery_handler_config: &ProtocolHandler,
-) -> Result, Error> {
- let query_var_set = ActualEnvVarQuery {};
- inner_get_discovery_handler(discovery_handler_config, &query_var_set)
-}
-
-fn inner_get_discovery_handler(
- discovery_handler_config: &ProtocolHandler,
- query: &impl EnvVarQuery,
-) -> Result, Error> {
- match discovery_handler_config {
- ProtocolHandler::onvif(onvif) => Ok(Box::new(onvif::OnvifDiscoveryHandler::new(&onvif))),
- ProtocolHandler::udev(udev) => Ok(Box::new(udev::UdevDiscoveryHandler::new(&udev))),
- ProtocolHandler::opcua(opcua) => Ok(Box::new(opcua::OpcuaDiscoveryHandler::new(&opcua))),
- ProtocolHandler::debugEcho(dbg) => match query.get_env_var("ENABLE_DEBUG_ECHO") {
- Ok(_) => Ok(Box::new(debug_echo::DebugEchoDiscoveryHandler::new(dbg))),
- _ => Err(failure::format_err!("No protocol configured")),
- },
- }
-}
-
-#[cfg(test)]
-mod test {
- use super::*;
- use akri_shared::{
- akri::configuration::{Configuration, ProtocolHandler},
- os::env_var::MockEnvVarQuery,
- };
- use std::env::VarError;
-
- #[tokio::test]
- async fn test_inner_get_discovery_handler() {
- let mock_query = MockEnvVarQuery::new();
-
- let onvif_json = r#"{"onvif":{}}"#;
- let deserialized: ProtocolHandler = serde_json::from_str(onvif_json).unwrap();
- assert!(inner_get_discovery_handler(&deserialized, &mock_query).is_ok());
-
- let udev_json = r#"{"udev":{"udevRules":[]}}"#;
- let deserialized: ProtocolHandler = serde_json::from_str(udev_json).unwrap();
- assert!(inner_get_discovery_handler(&deserialized, &mock_query).is_ok());
-
- let opcua_json = r#"{"opcua":{"opcuaDiscoveryMethod":{"standard":{}}}}"#;
- let deserialized: ProtocolHandler = serde_json::from_str(opcua_json).unwrap();
- assert!(inner_get_discovery_handler(&deserialized, &mock_query).is_ok());
-
- let json = r#"{}"#;
- assert!(serde_json::from_str::(json).is_err());
- }
-
- #[tokio::test]
- async fn test_udev_discover_no_rules() {
- let mock_query = MockEnvVarQuery::new();
-
- let json = r#"{"udev":{"udevRules":[]}}"#;
- let deserialized: ProtocolHandler = serde_json::from_str(json).unwrap();
- let discovery_handler = inner_get_discovery_handler(&deserialized, &mock_query).unwrap();
- assert_eq!(discovery_handler.discover().await.unwrap().len(), 0);
- }
-
- #[tokio::test]
- async fn test_factory_for_debug_echo_when_no_env_var_set() {
- let json = r#"{"protocol":{"debugEcho":{"descriptions":["foo1"],"shared":true}}}"#;
- let deserialized: Configuration = serde_json::from_str(json).unwrap();
-
- let mut mock_query_without_var_set = MockEnvVarQuery::new();
- mock_query_without_var_set
- .expect_get_env_var()
- .returning(|_| Err(VarError::NotPresent));
- if inner_get_discovery_handler(&deserialized.protocol, &mock_query_without_var_set).is_ok()
- {
- panic!("protocol configuration as debugEcho should return error when 'ENABLE_DEBUG_ECHO' env var is not set")
- }
-
- let mut mock_query_with_var_set = MockEnvVarQuery::new();
- mock_query_with_var_set
- .expect_get_env_var()
- .returning(|_| Ok("1".to_string()));
- let pi = DiscoveryResult::new(&"foo1".to_string(), HashMap::new(), true);
- let debug_echo_discovery_handler =
- inner_get_discovery_handler(&deserialized.protocol, &mock_query_with_var_set).unwrap();
- assert_eq!(true, debug_echo_discovery_handler.are_shared().unwrap());
- assert_eq!(
- 1,
- debug_echo_discovery_handler.discover().await.unwrap().len()
- );
- assert_eq!(
- pi.digest,
- debug_echo_discovery_handler
- .discover()
- .await
- .unwrap()
- .get(0)
- .unwrap()
- .digest
- );
- }
-
- #[tokio::test]
- async fn test_discovery_result_partialeq() {
- let left = DiscoveryResult::new(&"foo1".to_string(), HashMap::new(), true);
- let right = DiscoveryResult::new(&"foo1".to_string(), HashMap::new(), true);
- assert_eq!(left, right);
- }
-
- #[tokio::test]
- async fn test_discovery_result_partialeq_false() {
- {
- let left = DiscoveryResult::new(&"foo1".to_string(), HashMap::new(), true);
- let right = DiscoveryResult::new(&"foo2".to_string(), HashMap::new(), true);
- assert_ne!(left, right);
- }
-
- // TODO 201217: Needs work on `DiscoveryResult::new` to enable test (https://github.com/deislabs/akri/pull/176#discussion_r544703968)
- // {
- // std::env::set_var("AGENT_NODE_NAME", "something");
- // let left = DiscoveryResult::new(&"foo1".to_string(), HashMap::new(), true);
- // let right = DiscoveryResult::new(&"foo1".to_string(), HashMap::new(), false);
- // assert_ne!(left, right);
- // }
-
- {
- let mut nonempty: HashMap = HashMap::new();
- nonempty.insert("one".to_string(), "two".to_string());
- let left = DiscoveryResult::new(&"foo1".to_string(), nonempty, true);
- let right = DiscoveryResult::new(&"foo1".to_string(), HashMap::new(), true);
- assert_ne!(left, right);
- }
- }
-}
diff --git a/agent/src/protocols/onvif/discovery_handler.rs b/agent/src/protocols/onvif/discovery_handler.rs
deleted file mode 100644
index b46b1b8a3..000000000
--- a/agent/src/protocols/onvif/discovery_handler.rs
+++ /dev/null
@@ -1,511 +0,0 @@
-use super::super::{DiscoveryHandler, DiscoveryResult};
-use super::discovery_impl::util;
-use akri_shared::akri::configuration::{FilterList, FilterType, OnvifDiscoveryHandlerConfig};
-use akri_shared::onvif::device_info::{
- OnvifQuery, OnvifQueryImpl, ONVIF_DEVICE_IP_ADDRESS_LABEL_ID,
- ONVIF_DEVICE_MAC_ADDRESS_LABEL_ID, ONVIF_DEVICE_SERVICE_URL_LABEL_ID,
-};
-use async_trait::async_trait;
-use failure::Error;
-use std::{collections::HashMap, time::Duration};
-
-/// `OnvifDiscoveryHandler` discovers the onvif instances as described by the filters `discover_handler_config.ip_addresses`,
-/// `discover_handler_config.mac_addresses`, and `discover_handler_config.scopes`.
-/// The instances it discovers are always shared.
-#[derive(Debug)]
-pub struct OnvifDiscoveryHandler {
- discovery_handler_config: OnvifDiscoveryHandlerConfig,
-}
-
-impl OnvifDiscoveryHandler {
- pub fn new(discovery_handler_config: &OnvifDiscoveryHandlerConfig) -> Self {
- OnvifDiscoveryHandler {
- discovery_handler_config: discovery_handler_config.clone(),
- }
- }
-
- fn execute_filter(filter_list: Option<&FilterList>, filter_against: &[String]) -> bool {
- if filter_list.is_none() {
- return false;
- }
- let filter_action = filter_list.as_ref().unwrap().action.clone();
- let filter_count = filter_list
- .unwrap()
- .items
- .iter()
- .filter(|pattern| {
- filter_against
- .iter()
- .filter(|filter_against_item| filter_against_item.contains(*pattern))
- .count()
- > 0
- })
- .count();
-
- if FilterType::Include == filter_action {
- filter_count == 0
- } else {
- filter_count != 0
- }
- }
-
- async fn apply_filters(
- &self,
- device_service_uris: Vec,
- onvif_query: &impl OnvifQuery,
- ) -> Result, failure::Error> {
- let mut result = Vec::new();
- for device_service_url in device_service_uris.iter() {
- trace!("apply_filters - device service url {}", &device_service_url);
- let (ip_address, mac_address) = match onvif_query
- .get_device_ip_and_mac_address(&device_service_url)
- .await
- {
- Ok(ip_and_mac) => ip_and_mac,
- Err(e) => {
- error!("apply_filters - error getting ip and mac address: {}", e);
- continue;
- }
- };
-
- // Evaluate camera ip address against ip filter if provided
- let ip_address_as_vec = vec![ip_address.clone()];
- if OnvifDiscoveryHandler::execute_filter(
- self.discovery_handler_config.ip_addresses.as_ref(),
- &ip_address_as_vec,
- ) {
- continue;
- }
-
- // Evaluate camera mac address against mac filter if provided
- let mac_address_as_vec = vec![mac_address.clone()];
- if OnvifDiscoveryHandler::execute_filter(
- self.discovery_handler_config.mac_addresses.as_ref(),
- &mac_address_as_vec,
- ) {
- continue;
- }
-
- let ip_and_mac_joined = format!("{}-{}", &ip_address, &mac_address);
-
- // Evaluate camera scopes against scopes filter if provided
- let device_scopes = match onvif_query.get_device_scopes(&device_service_url).await {
- Ok(scopes) => scopes,
- Err(e) => {
- error!("apply_filters - error getting scopes: {}", e);
- continue;
- }
- };
- if OnvifDiscoveryHandler::execute_filter(
- self.discovery_handler_config.scopes.as_ref(),
- &device_scopes,
- ) {
- continue;
- }
-
- let mut properties = HashMap::new();
- properties.insert(
- ONVIF_DEVICE_SERVICE_URL_LABEL_ID.to_string(),
- device_service_url.to_string(),
- );
- properties.insert(ONVIF_DEVICE_IP_ADDRESS_LABEL_ID.into(), ip_address);
- properties.insert(ONVIF_DEVICE_MAC_ADDRESS_LABEL_ID.into(), mac_address);
-
- trace!(
- "apply_filters - returns DiscoveryResult ip/mac: {:?}, props: {:?}",
- &ip_and_mac_joined,
- &properties
- );
- result.push(DiscoveryResult::new(
- &ip_and_mac_joined,
- properties,
- self.are_shared().unwrap(),
- ))
- }
- Ok(result)
- }
-}
-
-#[async_trait]
-impl DiscoveryHandler for OnvifDiscoveryHandler {
- async fn discover(&self) -> Result, failure::Error> {
- let onvif_query = OnvifQueryImpl {};
-
- info!("discover - filters:{:?}", &self.discovery_handler_config,);
- let discovered_onvif_cameras = util::simple_onvif_discover(Duration::from_secs(
- self.discovery_handler_config.discovery_timeout_seconds as u64,
- ))
- .await?;
- info!("discover - discovered:{:?}", &discovered_onvif_cameras,);
- let filtered_onvif_cameras = self
- .apply_filters(discovered_onvif_cameras, &onvif_query)
- .await;
- info!("discover - filtered:{:?}", &filtered_onvif_cameras);
- filtered_onvif_cameras
- }
- fn are_shared(&self) -> Result {
- Ok(true)
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use akri_shared::onvif::device_info::test_onvif::MockOnvifQueryImpl;
-
- struct IpAndMac {
- mock_uri: &'static str,
- mock_ip: &'static str,
- mock_mac: &'static str,
- }
-
- struct Scope {
- mock_uri: &'static str,
- mock_scope: &'static str,
- }
-
- fn configure_scenario(
- mock: &mut MockOnvifQueryImpl,
- ip_and_mac: Option,
- scope: Option,
- ) {
- if let Some(ip_and_mac_) = ip_and_mac {
- configure_get_device_ip_and_mac_address(
- mock,
- &ip_and_mac_.mock_uri,
- &ip_and_mac_.mock_ip,
- &ip_and_mac_.mock_mac,
- )
- }
- if let Some(scope_) = scope {
- configure_get_device_scopes(mock, &scope_.mock_uri, &scope_.mock_scope)
- }
- }
-
- fn configure_get_device_ip_and_mac_address(
- mock: &mut MockOnvifQueryImpl,
- uri: &'static str,
- ip: &'static str,
- mac: &'static str,
- ) {
- mock.expect_get_device_ip_and_mac_address()
- .times(1)
- .withf(move |u| u == uri)
- .returning(move |_| Ok((ip.to_string(), mac.to_string())));
- }
-
- fn configure_get_device_scopes(
- mock: &mut MockOnvifQueryImpl,
- uri: &'static str,
- scope: &'static str,
- ) {
- mock.expect_get_device_scopes()
- .times(1)
- .withf(move |u| u == uri)
- .returning(move |_| Ok(vec![scope.to_string()]));
- }
-
- #[tokio::test]
- async fn test_apply_filters_no_filters() {
- let mock_uri = "device_uri";
-
- let mut mock = MockOnvifQueryImpl::new();
- configure_scenario(
- &mut mock,
- Some(IpAndMac {
- mock_uri: "device_uri",
- mock_ip: "mock.ip",
- mock_mac: "mock:mac",
- }),
- Some(Scope {
- mock_uri: "device_uri",
- mock_scope: "mock.scope",
- }),
- );
-
- let onvif = OnvifDiscoveryHandler::new(&OnvifDiscoveryHandlerConfig {
- ip_addresses: None,
- mac_addresses: None,
- scopes: None,
- discovery_timeout_seconds: 1,
- });
- let instances = onvif
- .apply_filters(vec![mock_uri.to_string()], &mock)
- .await
- .unwrap();
-
- assert_eq!(1, instances.len());
- }
-
- #[tokio::test]
- async fn test_apply_filters_include_ip_exist() {
- let mock_uri = "device_uri";
- let mock_ip = "mock.ip";
-
- let mut mock = MockOnvifQueryImpl::new();
- configure_scenario(
- &mut mock,
- Some(IpAndMac {
- mock_uri,
- mock_ip,
- mock_mac: "mock:mac",
- }),
- Some(Scope {
- mock_uri,
- mock_scope: "mock.scope",
- }),
- );
-
- let onvif = OnvifDiscoveryHandler::new(&OnvifDiscoveryHandlerConfig {
- ip_addresses: Some(FilterList {
- action: FilterType::Include,
- items: vec![mock_ip.to_string()],
- }),
- mac_addresses: None,
- scopes: None,
- discovery_timeout_seconds: 1,
- });
- let instances = onvif
- .apply_filters(vec![mock_uri.to_string()], &mock)
- .await
- .unwrap();
-
- assert_eq!(1, instances.len());
- }
-
- #[tokio::test]
- async fn test_apply_filters_include_ip_nonexist() {
- let mock_uri = "device_uri";
-
- let mut mock = MockOnvifQueryImpl::new();
- configure_scenario(
- &mut mock,
- Some(IpAndMac {
- mock_uri,
- mock_ip: "mock.ip",
- mock_mac: "mock:mac",
- }),
- None,
- );
-
- let onvif = OnvifDiscoveryHandler::new(&OnvifDiscoveryHandlerConfig {
- ip_addresses: Some(FilterList {
- action: FilterType::Include,
- items: vec!["nonexist.ip".to_string()],
- }),
- mac_addresses: None,
- scopes: None,
- discovery_timeout_seconds: 1,
- });
- let instances = onvif
- .apply_filters(vec![mock_uri.to_string()], &mock)
- .await
- .unwrap();
-
- assert_eq!(0, instances.len());
- }
-
- #[tokio::test]
- async fn test_apply_filters_exclude_ip_nonexist() {
- let mock_uri = "device_uri";
-
- let mut mock = MockOnvifQueryImpl::new();
- configure_scenario(
- &mut mock,
- Some(IpAndMac {
- mock_uri,
- mock_ip: "mock.ip",
- mock_mac: "mock:mac",
- }),
- Some(Scope {
- mock_uri,
- mock_scope: "mock.scope",
- }),
- );
-
- let onvif = OnvifDiscoveryHandler::new(&OnvifDiscoveryHandlerConfig {
- ip_addresses: Some(FilterList {
- action: FilterType::Exclude,
- items: vec!["nonexist.ip".to_string()],
- }),
- mac_addresses: None,
- scopes: None,
- discovery_timeout_seconds: 1,
- });
- let instances = onvif
- .apply_filters(vec![mock_uri.to_string()], &mock)
- .await
- .unwrap();
-
- assert_eq!(1, instances.len());
- }
-
- #[tokio::test]
- async fn test_apply_filters_exclude_ip_exist() {
- let mock_uri = "device_uri";
- let mock_ip = "mock.ip";
-
- let mut mock = MockOnvifQueryImpl::new();
- configure_scenario(
- &mut mock,
- Some(IpAndMac {
- mock_uri,
- mock_ip,
- mock_mac: "mock:mac",
- }),
- None,
- );
-
- let onvif = OnvifDiscoveryHandler::new(&OnvifDiscoveryHandlerConfig {
- ip_addresses: Some(FilterList {
- action: FilterType::Exclude,
- items: vec![mock_ip.to_string()],
- }),
- mac_addresses: None,
- scopes: None,
- discovery_timeout_seconds: 1,
- });
- let instances = onvif
- .apply_filters(vec![mock_uri.to_string()], &mock)
- .await
- .unwrap();
-
- assert_eq!(0, instances.len());
- }
-
- #[tokio::test]
- async fn test_apply_filters_include_mac_exist() {
- let mock_uri = "device_uri";
- let mock_mac = "mock:mac";
-
- let mut mock = MockOnvifQueryImpl::new();
- configure_scenario(
- &mut mock,
- Some(IpAndMac {
- mock_uri,
- mock_ip: "mock.ip",
- mock_mac,
- }),
- Some(Scope {
- mock_uri,
- mock_scope: "mock.scope",
- }),
- );
-
- let onvif = OnvifDiscoveryHandler::new(&OnvifDiscoveryHandlerConfig {
- ip_addresses: None,
- mac_addresses: Some(FilterList {
- action: FilterType::Include,
- items: vec![mock_mac.to_string()],
- }),
- scopes: None,
- discovery_timeout_seconds: 1,
- });
- let instances = onvif
- .apply_filters(vec![mock_uri.to_string()], &mock)
- .await
- .unwrap();
-
- assert_eq!(1, instances.len());
- }
-
- #[tokio::test]
- async fn test_apply_filters_include_mac_nonexist() {
- let mock_uri = "device_uri";
-
- let mut mock = MockOnvifQueryImpl::new();
- configure_scenario(
- &mut mock,
- Some(IpAndMac {
- mock_uri,
- mock_ip: "mock.ip",
- mock_mac: "mock:mac",
- }),
- None,
- );
-
- let onvif = OnvifDiscoveryHandler::new(&OnvifDiscoveryHandlerConfig {
- ip_addresses: None,
- mac_addresses: Some(FilterList {
- action: FilterType::Include,
- items: vec!["nonexist:mac".to_string()],
- }),
- scopes: None,
- discovery_timeout_seconds: 1,
- });
- let instances = onvif
- .apply_filters(vec![mock_uri.to_string()], &mock)
- .await
- .unwrap();
-
- assert_eq!(0, instances.len());
- }
-
- #[tokio::test]
- async fn test_apply_filters_exclude_mac_nonexist() {
- let mock_uri = "device_uri";
-
- let mut mock = MockOnvifQueryImpl::new();
- configure_scenario(
- &mut mock,
- Some(IpAndMac {
- mock_uri,
- mock_ip: "mock.ip",
- mock_mac: "mock:mac",
- }),
- Some(Scope {
- mock_uri,
- mock_scope: "mock.scope",
- }),
- );
-
- let onvif = OnvifDiscoveryHandler::new(&OnvifDiscoveryHandlerConfig {
- ip_addresses: None,
- mac_addresses: Some(FilterList {
- action: FilterType::Exclude,
- items: vec!["nonexist:mac".to_string()],
- }),
- scopes: None,
- discovery_timeout_seconds: 1,
- });
- let instances = onvif
- .apply_filters(vec![mock_uri.to_string()], &mock)
- .await
- .unwrap();
-
- assert_eq!(1, instances.len());
- }
-
- #[tokio::test]
- async fn test_apply_filters_exclude_mac_exist() {
- let mock_uri = "device_uri";
- let mock_mac = "mock:mac";
-
- let mut mock = MockOnvifQueryImpl::new();
- configure_scenario(
- &mut mock,
- Some(IpAndMac {
- mock_uri,
- mock_ip: "mock.ip",
- mock_mac,
- }),
- None,
- );
-
- let onvif = OnvifDiscoveryHandler::new(&OnvifDiscoveryHandlerConfig {
- ip_addresses: None,
- mac_addresses: Some(FilterList {
- action: FilterType::Exclude,
- items: vec![mock_mac.to_string()],
- }),
- scopes: None,
- discovery_timeout_seconds: 1,
- });
- let instances = onvif
- .apply_filters(vec![mock_uri.to_string()], &mock)
- .await
- .unwrap();
-
- assert_eq!(0, instances.len());
- }
-}
diff --git a/agent/src/protocols/onvif/mod.rs b/agent/src/protocols/onvif/mod.rs
deleted file mode 100644
index 5d3a2ee8f..000000000
--- a/agent/src/protocols/onvif/mod.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-mod discovery_handler;
-mod discovery_impl;
-pub use self::discovery_handler::OnvifDiscoveryHandler;
diff --git a/agent/src/protocols/opcua/discovery_handler.rs b/agent/src/protocols/opcua/discovery_handler.rs
deleted file mode 100644
index e0cbb1ad7..000000000
--- a/agent/src/protocols/opcua/discovery_handler.rs
+++ /dev/null
@@ -1,51 +0,0 @@
-use super::super::{DiscoveryHandler, DiscoveryResult};
-use super::{discovery_impl::do_standard_discovery, OPCUA_DISCOVERY_URL_LABEL};
-use akri_shared::akri::configuration::{OpcuaDiscoveryHandlerConfig, OpcuaDiscoveryMethod};
-use async_trait::async_trait;
-use failure::Error;
-
-/// `OpcuaDiscoveryHandler` discovers the OPC UA server instances as described by the `discovery_handler_config.opcua_discovery_method`
-/// and the filter `discover_handler_config.application_names`. The instances it discovers are always shared.
-#[derive(Debug)]
-pub struct OpcuaDiscoveryHandler {
- discovery_handler_config: OpcuaDiscoveryHandlerConfig,
-}
-
-impl OpcuaDiscoveryHandler {
- pub fn new(discovery_handler_config: &OpcuaDiscoveryHandlerConfig) -> Self {
- OpcuaDiscoveryHandler {
- discovery_handler_config: discovery_handler_config.clone(),
- }
- }
-}
-
-#[async_trait]
-impl DiscoveryHandler for OpcuaDiscoveryHandler {
- async fn discover(&self) -> Result, Error> {
- let discovery_urls: Vec =
- match &self.discovery_handler_config.opcua_discovery_method {
- OpcuaDiscoveryMethod::standard(standard_opcua_discovery) => do_standard_discovery(
- standard_opcua_discovery.discovery_urls.clone(),
- self.discovery_handler_config.application_names.clone(),
- ),
- // No other discovery methods implemented yet
- };
-
- // Build DiscoveryResult for each server discovered
- Ok(discovery_urls
- .into_iter()
- .map(|discovery_url| {
- let mut properties = std::collections::HashMap::new();
- trace!(
- "discover - found OPC UA server at DiscoveryURL {}",
- discovery_url
- );
- properties.insert(OPCUA_DISCOVERY_URL_LABEL.to_string(), discovery_url.clone());
- DiscoveryResult::new(&discovery_url, properties, self.are_shared().unwrap())
- })
- .collect::>())
- }
- fn are_shared(&self) -> Result {
- Ok(true)
- }
-}
diff --git a/agent/src/protocols/udev/discovery_handler.rs b/agent/src/protocols/udev/discovery_handler.rs
deleted file mode 100644
index 422d64f76..000000000
--- a/agent/src/protocols/udev/discovery_handler.rs
+++ /dev/null
@@ -1,57 +0,0 @@
-use super::super::{DiscoveryHandler, DiscoveryResult};
-use super::{discovery_impl, udev_enumerator, UDEV_DEVNODE_LABEL_ID};
-use akri_shared::akri::configuration::UdevDiscoveryHandlerConfig;
-use async_trait::async_trait;
-use failure::Error;
-use std::collections::HashSet;
-
-/// `UdevDiscoveryHandler` discovers udev instances by parsing the udev rules in `discovery_handler_config.udev_rules`.
-/// The instances it discovers are always unshared.
-#[derive(Debug)]
-pub struct UdevDiscoveryHandler {
- discovery_handler_config: UdevDiscoveryHandlerConfig,
-}
-
-impl UdevDiscoveryHandler {
- pub fn new(discovery_handler_config: &UdevDiscoveryHandlerConfig) -> Self {
- UdevDiscoveryHandler {
- discovery_handler_config: discovery_handler_config.clone(),
- }
- }
-}
-
-#[async_trait]
-impl DiscoveryHandler for UdevDiscoveryHandler {
- async fn discover(&self) -> Result, Error> {
- let udev_rules = self.discovery_handler_config.udev_rules.clone();
- trace!("discover - for udev rules {:?}", udev_rules);
- let mut devpaths: HashSet = HashSet::new();
- udev_rules
- .iter()
- .map(|rule| {
- let enumerator = udev_enumerator::create_enumerator();
- let paths = discovery_impl::do_parse_and_find(enumerator, &rule)?;
- paths.into_iter().for_each(|path| {
- devpaths.insert(path);
- });
- Ok(())
- })
- .collect::>()?;
- trace!(
- "discover - mapping and returning devices at devpaths {:?}",
- devpaths
- );
- Ok(devpaths
- .into_iter()
- .map(|path| {
- let mut properties = std::collections::HashMap::new();
- properties.insert(UDEV_DEVNODE_LABEL_ID.to_string(), path.clone());
- DiscoveryResult::new(&path, properties, self.are_shared().unwrap())
- })
- .collect::>())
- }
-
- fn are_shared(&self) -> Result {
- Ok(false)
- }
-}
diff --git a/agent/src/util/config_action.rs b/agent/src/util/config_action.rs
index ac01bbfa0..89a8ac798 100644
--- a/agent/src/util/config_action.rs
+++ b/agent/src/util/config_action.rs
@@ -1,33 +1,23 @@
-use super::super::protocols;
use super::{
constants::{
- DEVICE_PLUGIN_PATH, DISCOVERY_DELAY_SECS, SHARED_INSTANCE_OFFLINE_GRACE_PERIOD_SECS,
+ DISCOVERY_OPERATOR_FINISHED_DISCOVERY_CHANNEL_CAPACITY,
+ DISCOVERY_OPERATOR_STOP_DISCOVERY_CHANNEL_CAPACITY,
},
device_plugin_service,
- device_plugin_service::{
- get_device_instance_name, ConnectivityStatus, InstanceInfo, InstanceMap,
- },
+ device_plugin_service::InstanceMap,
+ discovery_operator::start_discovery::{start_discovery, DiscoveryOperator},
+ registration::RegisteredDiscoveryHandlerMap,
};
use akri_shared::{
- akri::{
- configuration::{Configuration, KubeAkriConfig, ProtocolHandler},
- API_CONFIGURATIONS, API_NAMESPACE, API_VERSION,
- },
+ akri::{configuration::KubeAkriConfig, API_CONFIGURATIONS, API_NAMESPACE, API_VERSION},
k8s,
- k8s::KubeInterface,
+ k8s::{try_delete_instance, KubeInterface},
};
use futures::StreamExt;
use kube::api::{Informer, RawApi, WatchEvent};
use log::{info, trace};
-use std::{
- collections::HashMap,
- sync::Arc,
- time::{Duration, Instant},
-};
-use tokio::{
- sync::{broadcast, mpsc, Mutex},
- time::timeout,
-};
+use std::{collections::HashMap, sync::Arc};
+use tokio::sync::{broadcast, mpsc, Mutex};
type ConfigMap = Arc>>;
@@ -35,13 +25,22 @@ type ConfigMap = Arc>>;
/// and senders for ceasing to discover instances upon Configuration deletion.
#[derive(Debug)]
pub struct ConfigInfo {
+ /// Map of all of a Configuration's Instances
instance_map: InstanceMap,
- stop_discovery_sender: mpsc::Sender<()>,
- finished_discovery_sender: broadcast::Sender<()>,
+ /// Sends notification to a `DiscoveryOperator` that it should stop all discovery for its Configuration.
+ /// This signals it to tell each of its subtasks to stop discovery.
+ /// A broadcast channel is used so both the sending and receiving ends can be cloned.
+ stop_discovery_sender: broadcast::Sender<()>,
+ /// Receives notification that all `DiscoveryOperators` threads have completed and a Configuration's Instances
+ /// can be safely deleted and the associated `DevicePluginServices` terminated.
+ finished_discovery_receiver: mpsc::Receiver<()>,
}
/// This handles pre-existing Configurations and invokes an internal method that watches for Configuration events.
-pub async fn do_config_watch() -> Result<(), Box> {
+pub async fn do_config_watch(
+ discovery_handler_map: RegisteredDiscoveryHandlerMap,
+ new_discovery_handler_sender: broadcast::Sender,
+) -> Result<(), Box> {
info!("do_config_watch - enter");
let config_map: ConfigMap = Arc::new(Mutex::new(HashMap::new()));
let kube_interface = k8s::create_kube_interface();
@@ -51,16 +50,31 @@ pub async fn do_config_watch() -> Result<(), Box Result<(), Box,
) -> Result<(), Box> {
trace!("watch_for_config_changes - start");
let akri_config_type = RawApi::customResource(API_CONFIGURATIONS)
@@ -86,7 +102,15 @@ async fn watch_for_config_changes(
// Currently, this does not handle None except to break the
// while.
while let Some(event) = configs.next().await {
- handle_config(kube_interface, event?, config_map.clone()).await?
+ let new_discovery_handler_sender = new_discovery_handler_sender.clone();
+ handle_config(
+ kube_interface,
+ event?,
+ config_map.clone(),
+ discovery_handler_map.clone(),
+ new_discovery_handler_sender,
+ )
+ .await?
}
}
}
@@ -97,6 +121,8 @@ async fn handle_config(
kube_interface: &impl KubeInterface,
event: WatchEvent,
config_map: ConfigMap,
+ discovery_handler_map: RegisteredDiscoveryHandlerMap,
+ new_discovery_handler_sender: broadcast::Sender,
) -> Result<(), Box> {
trace!("handle_config - something happened to a configuration");
match event {
@@ -106,7 +132,15 @@ async fn handle_config(
config.metadata.name
);
tokio::spawn(async move {
- handle_config_add(&config, config_map).await.unwrap();
+ handle_config_add(
+ Arc::new(Box::new(k8s::create_kube_interface())),
+ &config,
+ config_map,
+ discovery_handler_map,
+ new_discovery_handler_sender,
+ )
+ .await
+ .unwrap();
});
Ok(())
}
@@ -126,7 +160,15 @@ async fn handle_config(
);
handle_config_delete(kube_interface, &config, config_map.clone()).await?;
tokio::spawn(async move {
- handle_config_add(&config, config_map).await.unwrap();
+ handle_config_add(
+ Arc::new(Box::new(k8s::create_kube_interface())),
+ &config,
+ config_map,
+ discovery_handler_map,
+ new_discovery_handler_sender,
+ )
+ .await
+ .unwrap();
});
Ok(())
}
@@ -140,65 +182,52 @@ async fn handle_config(
/// This handles added Configuration by creating a new ConfigInfo for it and adding it to the ConfigMap.
/// Then calls a function to continually observe the availability of instances associated with the Configuration.
async fn handle_config_add(
+ kube_interface: Arc>,
config: &KubeAkriConfig,
config_map: ConfigMap,
+ discovery_handler_map: RegisteredDiscoveryHandlerMap,
+ new_discovery_handler_sender: broadcast::Sender,
) -> Result<(), Box> {
- let config_protocol = config.spec.protocol.clone();
- let discovery_handler = protocols::get_discovery_handler(&config_protocol)?;
- let discovery_results = discovery_handler.discover().await?;
let config_name = config.metadata.name.clone();
- let config_uid = config.metadata.uid.as_ref().unwrap().clone();
- let config_namespace = config.metadata.namespace.as_ref().unwrap().clone();
- info!(
- "handle_config_add - entered for Configuration {} with visible_instances={:?}",
- config.metadata.name, &discovery_results
- );
// Create a new instance map for this config and add it to the config map
let instance_map: InstanceMap = Arc::new(Mutex::new(HashMap::new()));
- // Channel capacity: should only ever be sent once upon config deletion
- let (stop_discovery_sender, stop_discovery_receiver) = mpsc::channel(1);
- // Channel capacity: should only ever be sent once upon receiving stop watching message
- let (finished_discovery_sender, _) = broadcast::channel(1);
+ let (stop_discovery_sender, _): (broadcast::Sender<()>, broadcast::Receiver<()>) =
+ broadcast::channel(DISCOVERY_OPERATOR_STOP_DISCOVERY_CHANNEL_CAPACITY);
+ let (mut finished_discovery_sender, finished_discovery_receiver) =
+ mpsc::channel(DISCOVERY_OPERATOR_FINISHED_DISCOVERY_CHANNEL_CAPACITY);
let config_info = ConfigInfo {
instance_map: instance_map.clone(),
- stop_discovery_sender,
- finished_discovery_sender: finished_discovery_sender.clone(),
+ stop_discovery_sender: stop_discovery_sender.clone(),
+ finished_discovery_receiver,
};
config_map
.lock()
.await
.insert(config_name.clone(), config_info);
- let kube_interface = k8s::create_kube_interface();
- let config_spec = config.spec.clone();
+ let config = config.clone();
// Keep discovering instances until the config is deleted, signaled by a message from handle_config_delete
tokio::spawn(async move {
- let periodic_discovery = PeriodicDiscovery {
- config_name,
- config_uid,
- config_namespace,
- config_spec,
- config_protocol,
- instance_map,
- };
- periodic_discovery
- .do_periodic_discovery(
- &kube_interface,
- stop_discovery_receiver,
- finished_discovery_sender,
- DEVICE_PLUGIN_PATH,
- )
- .await
- .unwrap();
+ let discovery_operator =
+ DiscoveryOperator::new(discovery_handler_map, config, instance_map);
+ start_discovery(
+ discovery_operator,
+ new_discovery_handler_sender,
+ stop_discovery_sender,
+ &mut finished_discovery_sender,
+ kube_interface,
+ )
+ .await
+ .unwrap();
})
.await?;
Ok(())
}
-/// This handles a deleted Congfiguration. First, it ceases to discover instances associated with the Configuration.
+/// This handles a deleted Configuration. First, it ceases to discover instances associated with the Configuration.
/// Then, for each of the Configuration's Instances, it signals the DevicePluginService to shutdown,
/// and deletes the Instance CRD.
-pub async fn handle_config_delete(
+async fn handle_config_delete(
kube_interface: &impl KubeInterface,
config: &KubeAkriConfig,
config_map: ConfigMap,
@@ -216,17 +245,17 @@ pub async fn handle_config_delete(
.stop_discovery_sender
.clone()
.send(())
- .await
.is_ok()
{
- let mut finished_discovery_receiver = config_map
+ config_map
.lock()
.await
- .get(&config.metadata.name)
+ .get_mut(&config.metadata.name)
.unwrap()
- .finished_discovery_sender
- .subscribe();
- finished_discovery_receiver.recv().await.unwrap();
+ .finished_discovery_receiver
+ .recv()
+ .await
+ .unwrap();
trace!(
"handle_config_delete - for config {} received message that do_periodic_discovery ended",
config.metadata.name
@@ -249,8 +278,16 @@ pub async fn handle_config_delete(
.clone();
config_map_locked.remove(&config.metadata.name);
}
+ delete_all_instances_in_map(kube_interface, instance_map, config).await?;
+ Ok(())
+}
- // Shutdown Instances' DevicePluginServices and delete the Instances
+/// This shuts down all a Configuration's Instances and terminates the associated Device Plugins
+pub async fn delete_all_instances_in_map(
+ kube_interface: &impl k8s::KubeInterface,
+ instance_map: InstanceMap,
+ config: &KubeAkriConfig,
+) -> Result<(), Box> {
let mut instance_map_locked = instance_map.lock().await;
let instances_to_delete_map = instance_map_locked.clone();
let namespace = config.metadata.namespace.as_ref().unwrap();
@@ -267,305 +304,49 @@ pub async fn handle_config_delete(
instance_map_locked.remove(&instance_name);
try_delete_instance(kube_interface, &instance_name, &namespace).await?;
}
-
Ok(())
}
-/// This deletes an Instance unless it has already been deleted by another node
-async fn try_delete_instance(
- kube_interface: &impl KubeInterface,
- instance_name: &str,
- instance_namespace: &str,
-) -> Result<(), Box> {
- match kube_interface
- .delete_instance(instance_name, &instance_namespace)
- .await
- {
- Ok(()) => {
- trace!("delete_instance - deleted Instance {}", instance_name);
- Ok(())
- }
- Err(e) => {
- // Check if already was deleted else return error
- if let Err(_e) = kube_interface
- .find_instance(&instance_name, &instance_namespace)
- .await
- {
- trace!(
- "delete_instance - discovered Instance {} already deleted",
- instance_name
- );
- Ok(())
- } else {
- Err(e)
- }
- }
- }
-}
-
-/// Information required for periodic discovery
-struct PeriodicDiscovery {
- config_name: String,
- config_uid: String,
- config_namespace: String,
- config_spec: Configuration,
- config_protocol: ProtocolHandler,
- instance_map: InstanceMap,
-}
-
-impl PeriodicDiscovery {
- /// This is spawned as a task for each Configuration and continues to periodically run
- /// until the Config is deleted, at which point, this function is signaled to stop.
- /// Looks up which instances are currently visible to the node. Passes this list to a function that
- /// updates the ConnectivityStatus of the Configuration's Instances or deletes Instance CRDs if needed.
- /// If a new instance becomes visible that isn't in the Configuration's InstanceMap,
- /// a DevicePluginService and Instance CRD are created for it, and it is added to the InstanceMap.
- async fn do_periodic_discovery(
- &self,
- kube_interface: &impl KubeInterface,
- mut stop_discovery_receiver: mpsc::Receiver<()>,
- finished_discovery_sender: broadcast::Sender<()>,
- device_plugin_path: &str,
- ) -> Result<(), Box> {
- trace!(
- "do_periodic_discovery - start for config {}",
- self.config_name
- );
- let protocol = protocols::get_discovery_handler(&self.config_protocol)?;
- let shared = protocol.are_shared()?;
- loop {
- trace!(
- "do_periodic_discovery - loop iteration for config {}",
- &self.config_name
- );
- let discovery_results = protocol.discover().await?;
- let config_name = self.config_name.clone();
- let currently_visible_instances: HashMap =
- discovery_results
- .iter()
- .map(|discovery_result| {
- let instance_name =
- get_device_instance_name(&discovery_result.digest, &config_name);
- (instance_name, discovery_result.clone())
- })
- .collect();
-
- // Update the connectivity status of instances and return list of visible instances that don't have Instance CRDs
- let new_discovery_results = self
- .update_connectivity_status(kube_interface, ¤tly_visible_instances, shared)
- .await?;
-
- // If there are newly visible instances associated with a Config, make a device plugin and Instance CRD for them
- if !new_discovery_results.is_empty() {
- for discovery_result in new_discovery_results {
- let config_name = config_name.clone();
- let instance_name =
- get_device_instance_name(&discovery_result.digest, &config_name);
- trace!(
- "do_periodic_discovery - new instance {} came online",
- instance_name
- );
- let instance_properties = discovery_result.properties.clone();
- let config_spec = self.config_spec.clone();
- let instance_map = self.instance_map.clone();
- if let Err(e) = device_plugin_service::build_device_plugin(
- instance_name,
- config_name,
- self.config_uid.clone(),
- self.config_namespace.clone(),
- config_spec,
- shared,
- instance_properties,
- instance_map,
- device_plugin_path,
- )
- .await
- {
- error!("do_periodic_discovery - error {} building device plugin ... trying again on next iteration", e);
- }
- }
- }
- if timeout(
- Duration::from_secs(DISCOVERY_DELAY_SECS),
- stop_discovery_receiver.recv(),
- )
- .await
- .is_ok()
- {
- trace!("do_periodic_discovery - for config {} received message to end ... sending message that finished and returning Ok", config_name);
- finished_discovery_sender.send(()).unwrap();
- return Ok(());
- };
- }
- }
-
- /// Takes in a list of currently visible instances and either updates an Instance's ConnectivityStatus or deletes an Instance.
- /// If an instance is no longer visible then it's ConnectivityStatus is changed to Offline(time now).
- /// The associated DevicePluginService checks its ConnectivityStatus before sending a response back to kubelet
- /// and will send all unhealthy devices if its status is Offline, preventing kubelet from allocating any more pods to it.
- /// An Instance CRD is deleted and it's DevicePluginService shutdown if its:
- /// (A) shared instance is still not visible after 5 minutes or (B) unshared instance is still not visible on the next visibility check.
- /// An unshared instance will be offline for between DISCOVERY_DELAY_SECS - 2 x DISCOVERY_DELAY_SECS
- async fn update_connectivity_status(
- &self,
- kube_interface: &impl KubeInterface,
- currently_visible_instances: &HashMap,
- shared: bool,
- ) -> Result, Box>
- {
- let instance_map_clone = self.instance_map.lock().await.clone();
- // Find all visible instances that do not have Instance CRDs yet
- let new_discovery_results: Vec = currently_visible_instances
- .iter()
- .filter(|(name, _)| !instance_map_clone.contains_key(*name))
- .map(|(_, p)| p.clone())
- .collect();
-
- for (instance, instance_info) in instance_map_clone {
- if currently_visible_instances.contains_key(&instance) {
- let connectivity_status = instance_info.connectivity_status;
- // If instance is visible, make sure connectivity status is (updated to be) Online
- if let ConnectivityStatus::Offline(_instant) = connectivity_status {
- trace!(
- "update_connectivity_status - instance {} that was temporarily offline is back online",
- instance
- );
- let list_and_watch_message_sender = instance_info.list_and_watch_message_sender;
- let updated_instance_info = InstanceInfo {
- connectivity_status: ConnectivityStatus::Online,
- list_and_watch_message_sender: list_and_watch_message_sender.clone(),
- };
- self.instance_map
- .lock()
- .await
- .insert(instance.clone(), updated_instance_info);
- list_and_watch_message_sender
- .send(device_plugin_service::ListAndWatchMessageKind::Continue)
- .unwrap();
- }
- trace!(
- "update_connectivity_status - instance {} still online",
- instance
- );
- } else {
- // If the instance is not visible:
- // // If the instance has not already been labeled offline, label it
- // // If the instance has already been labeled offline
- // // // shared - remove instance from map if grace period has elaspsed without the instance coming back online
- // // // unshared - remove instance from map
- match instance_info.connectivity_status {
- ConnectivityStatus::Online => {
- let sender = instance_info.list_and_watch_message_sender.clone();
- let updated_instance_info = InstanceInfo {
- connectivity_status: ConnectivityStatus::Offline(Instant::now()),
- list_and_watch_message_sender: instance_info
- .list_and_watch_message_sender,
- };
- self.instance_map
- .lock()
- .await
- .insert(instance.clone(), updated_instance_info);
- trace!(
- "update_connectivity_status - instance {} went offline ... starting timer and forcing list_and_watch to continue",
- instance
- );
- sender
- .send(device_plugin_service::ListAndWatchMessageKind::Continue)
- .unwrap();
- }
- ConnectivityStatus::Offline(instant) => {
- let time_offline = instant.elapsed().as_secs();
- // If instance has been offline for longer than the grace period or it is unshared, terminate the associated device plugin
- if !shared || time_offline >= SHARED_INSTANCE_OFFLINE_GRACE_PERIOD_SECS {
- trace!("update_connectivity_status - instance {} has been offline too long ... terminating DevicePluginService", instance);
- device_plugin_service::terminate_device_plugin_service(
- &instance,
- self.instance_map.clone(),
- )
- .await?;
- try_delete_instance(kube_interface, &instance, &self.config_namespace)
- .await?;
- }
- }
- }
- }
- }
- Ok(new_discovery_results)
- }
-}
-
#[cfg(test)]
mod config_action_tests {
+ use super::super::{
+ device_plugin_service,
+ device_plugin_service::{InstanceConnectivityStatus, InstanceMap},
+ discovery_operator::tests::{add_discovery_handler_to_map, build_instance_map},
+ registration::{DiscoveryHandlerEndpoint, DiscoveryHandlerStatus},
+ };
use super::*;
- use akri_shared::k8s::test_kube::MockKubeImpl;
- use protocols::debug_echo::{DEBUG_ECHO_AVAILABILITY_CHECK_PATH, OFFLINE};
- use std::{env, fs};
- use tempfile::Builder;
- use tokio::sync::broadcast;
-
- async fn build_instance_map(
- config: &KubeAkriConfig,
- visibile_discovery_results: &mut Vec,
- list_and_watch_message_receivers: &mut Vec<
- broadcast::Receiver,
- >,
- connectivity_status: ConnectivityStatus,
- ) -> InstanceMap {
- // Set env vars for getting instances
- env::set_var("AGENT_NODE_NAME", "node-a");
- env::set_var("ENABLE_DEBUG_ECHO", "yes");
- let protocol = config.spec.protocol.clone();
- let discovery_handler = protocols::get_discovery_handler(&protocol).unwrap();
- let discovery_results = discovery_handler.discover().await.unwrap();
- *visibile_discovery_results = discovery_results.clone();
- let instance_map: InstanceMap = Arc::new(Mutex::new(
- discovery_results
- .iter()
- .map(|instance_info| {
- let (list_and_watch_message_sender, list_and_watch_message_receiver) =
- broadcast::channel(2);
- list_and_watch_message_receivers.push(list_and_watch_message_receiver);
- let instance_name =
- get_device_instance_name(&instance_info.digest, &config.metadata.name);
- (
- instance_name,
- InstanceInfo {
- list_and_watch_message_sender,
- connectivity_status: connectivity_status.clone(),
- },
- )
- })
- .collect(),
- ));
- instance_map
- }
+ use akri_discovery_utils::discovery::{mock_discovery_handler, v0::Device};
+ use akri_shared::{akri::configuration::KubeAkriConfig, k8s::MockKubeInterface};
+ use std::{collections::HashMap, fs, sync::Arc};
+ use tokio::sync::{broadcast, Mutex};
#[tokio::test]
async fn test_handle_config_delete() {
let _ = env_logger::builder().is_test(true).try_init();
- let path_to_config = "../test/json/config-a.json";
- let dcc_json = fs::read_to_string(path_to_config).expect("Unable to read file");
- let config: KubeAkriConfig = serde_json::from_str(&dcc_json).unwrap();
+ let path_to_config = "../test/yaml/config-a.yaml";
+ let config_yaml = fs::read_to_string(path_to_config).expect("Unable to read file");
+ let config: KubeAkriConfig = serde_yaml::from_str(&config_yaml).unwrap();
let config_name = config.metadata.name.clone();
let mut list_and_watch_message_receivers = Vec::new();
let mut visible_discovery_results = Vec::new();
- let mut mock = MockKubeImpl::new();
+ let mut mock = MockKubeInterface::new();
let instance_map: InstanceMap = build_instance_map(
&config,
&mut visible_discovery_results,
&mut list_and_watch_message_receivers,
- ConnectivityStatus::Online,
+ InstanceConnectivityStatus::Online,
)
.await;
- let (stop_discovery_sender, mut stop_discovery_receiver) = mpsc::channel(2);
- let (finished_discovery_sender, _) = broadcast::channel(2);
+ let (stop_discovery_sender, mut stop_discovery_receiver) = broadcast::channel(2);
+ let (mut finished_discovery_sender, finished_discovery_receiver) = mpsc::channel(2);
let mut map: HashMap = HashMap::new();
map.insert(
config_name.clone(),
ConfigInfo {
stop_discovery_sender,
instance_map: instance_map.clone(),
- finished_discovery_sender: finished_discovery_sender.clone(),
+ finished_discovery_receiver,
},
);
let config_map: ConfigMap = Arc::new(Mutex::new(map));
@@ -581,10 +362,10 @@ mod config_action_tests {
assert!(!config_map.lock().await.contains_key(&config_name));
});
- // Assert that handle_config_delete tells do_periodic_discovery to end
- assert!(stop_discovery_receiver.recv().await.is_some());
+ // Assert that handle_config_delete tells start_discovery to end
+ assert!(stop_discovery_receiver.recv().await.is_ok());
// Mimic do_periodic_discovery's response
- finished_discovery_sender.send(()).unwrap();
+ finished_discovery_sender.send(()).await.unwrap();
// Assert list_and_watch is signaled to end for every instance associated with a config
let mut tasks = Vec::new();
@@ -602,209 +383,175 @@ mod config_action_tests {
assert_eq!(instance_map.lock().await.len(), 0);
}
- // 1: ConnectivityStatus of all instances that go offline is changed from Online to Offline
- // 2: ConnectivityStatus of shared instances that come back online in under 5 minutes is changed from Offline to Online
- // 3: ConnectivityStatus of unshared instances that come back online before next periodic discovery is changed from Offline to Online
- #[tokio::test]
- async fn test_update_connectivity_status() {
- let _ = env_logger::builder().is_test(true).try_init();
- let path_to_config = "../test/json/config-a.json";
- let dcc_json = fs::read_to_string(path_to_config).expect("Unable to read file");
- let config: KubeAkriConfig = serde_json::from_str(&dcc_json).unwrap();
- let config_name = config.metadata.name.clone();
- let mut list_and_watch_message_receivers = Vec::new();
- let mut visible_discovery_results = Vec::new();
- let mock = MockKubeImpl::new();
-
- //
- // 1: Assert that ConnectivityStatus of instance that are no longer visible is changed to Offline
- //
- let instance_map: InstanceMap = build_instance_map(
- &config,
- &mut visible_discovery_results,
- &mut list_and_watch_message_receivers,
- ConnectivityStatus::Online,
- )
- .await;
- let shared = true;
- // discover returns an empty vector when instances are offline
- let no_visible_instances: HashMap = HashMap::new();
- let periodic_dicovery = PeriodicDiscovery {
- config_name: config_name.clone(),
- config_uid: config.metadata.uid.as_ref().unwrap().clone(),
- config_namespace: config.metadata.namespace.as_ref().unwrap().clone(),
- config_spec: config.spec.clone(),
- config_protocol: config.spec.protocol.clone(),
- instance_map: instance_map.clone(),
- };
- periodic_dicovery
- .update_connectivity_status(&mock, &no_visible_instances, shared)
+ async fn run_and_test_handle_config_add(
+ discovery_handler_map: RegisteredDiscoveryHandlerMap,
+ config_map: ConfigMap,
+ config: KubeAkriConfig,
+ dh_endpoint: &DiscoveryHandlerEndpoint,
+ dh_name: &str,
+ ) -> tokio::task::JoinHandle<()> {
+ let (new_discovery_handler_sender, _) = broadcast::channel(1);
+ let mut mock_kube_interface = MockKubeInterface::new();
+ mock_kube_interface
+ .expect_create_instance()
+ .times(1)
+ .returning(move |_, _, _, _, _| Ok(()));
+ let arc_mock_kube_interface: Arc> =
+ Arc::new(Box::new(mock_kube_interface));
+ let config_add_config = config.clone();
+ let config_add_config_map = config_map.clone();
+ let config_add_discovery_handler_map = discovery_handler_map.clone();
+ let handle = tokio::spawn(async move {
+ handle_config_add(
+ arc_mock_kube_interface,
+ &config_add_config,
+ config_add_config_map,
+ config_add_discovery_handler_map,
+ new_discovery_handler_sender,
+ )
.await
.unwrap();
- let unwrapped_instance_map = instance_map.lock().await.clone();
- for (_, instance_info) in unwrapped_instance_map {
- assert_ne!(
- instance_info.connectivity_status,
- ConnectivityStatus::Online
- );
- }
+ });
- //
- // 2: Assert that ConnectivityStatus of shared instances that come back online in <5 mins is changed to Online
- //
- let instance_map: InstanceMap = build_instance_map(
- &config,
- &mut visible_discovery_results,
- &mut list_and_watch_message_receivers,
- ConnectivityStatus::Offline(Instant::now()),
+ // Loop until the Configuration and single discovered Instance are added to the ConfigMap
+ let mut x: i8 = 0;
+ while x < 5 {
+ tokio::time::delay_for(std::time::Duration::from_millis(200)).await;
+ if let Some(config_info) = config_map.lock().await.get(&config.metadata.name) {
+ if config_info.instance_map.lock().await.len() == 1 {
+ break;
+ }
+ }
+ x += 1;
+ }
+ assert_ne!(x, 4);
+ // Assert that Discovery Handler is marked as Active
+ check_discovery_handler_status(
+ discovery_handler_map,
+ dh_name,
+ dh_endpoint,
+ DiscoveryHandlerStatus::Active,
)
.await;
- let shared = true;
- let currently_visible_instances: HashMap =
- visible_discovery_results
- .iter()
- .map(|instance_info| {
- let instance_name =
- get_device_instance_name(&instance_info.digest, &config_name);
- (instance_name, instance_info.clone())
- })
- .collect();
- let periodic_dicovery = PeriodicDiscovery {
- config_name: config_name.clone(),
- config_uid: config.metadata.uid.as_ref().unwrap().clone(),
- config_namespace: config.metadata.namespace.as_ref().unwrap().clone(),
- config_spec: config.spec.clone(),
- config_protocol: config.spec.protocol.clone(),
- instance_map: instance_map.clone(),
- };
- periodic_dicovery
- .update_connectivity_status(&mock, ¤tly_visible_instances, shared)
- .await
- .unwrap();
- let unwrapped_instance_map = instance_map.lock().await.clone();
- for (_, instance_info) in unwrapped_instance_map {
- assert_eq!(
- instance_info.connectivity_status,
- ConnectivityStatus::Online
- );
+ handle
+ }
+
+ async fn check_discovery_handler_status(
+ discovery_handler_map: RegisteredDiscoveryHandlerMap,
+ dh_name: &str,
+ dh_endpoint: &DiscoveryHandlerEndpoint,
+ dh_status: DiscoveryHandlerStatus,
+ ) {
+ let mut x: i8 = 0;
+ while x < 5 {
+ tokio::time::delay_for(std::time::Duration::from_millis(200)).await;
+ let dh_map = discovery_handler_map.lock().unwrap();
+ if let Some(dh_details_map) = dh_map.get(dh_name) {
+ if dh_details_map.get(dh_endpoint).unwrap().connectivity_status == dh_status {
+ break;
+ }
+ }
+ x += 1;
}
+ assert_ne!(x, 4);
+ }
- //
- // 3: Assert that ConnectivityStatus of unshared instances that come back online before next visibility check is changed to Online
- //
- let instance_map: InstanceMap = build_instance_map(
- &config,
- &mut visible_discovery_results,
- &mut list_and_watch_message_receivers,
- ConnectivityStatus::Offline(Instant::now()),
+ // Tests that when a Configuration is added, deleted, and added again,
+ // instances are created, deleted and recreated,
+ // and the Discovery Handler is marked as Active, Waiting, Active, and Waiting.
+ // Also asserts that all threads are successfully terminated.
+ #[tokio::test]
+ async fn test_handle_config_add_delete_add() {
+ let _ = env_logger::builder().is_test(true).try_init();
+
+ // Set up Discovery Handler
+ // Start a mock DH, specifying that it should NOT return an error
+ let return_error = false;
+ let (endpoint_dir, endpoint) =
+ mock_discovery_handler::get_mock_discovery_handler_dir_and_endpoint("mock.sock");
+ let dh_endpoint = DiscoveryHandlerEndpoint::Uds(endpoint.to_string());
+ let device_id = "device_id";
+ let _dh_server_thread_handle = mock_discovery_handler::run_mock_discovery_handler(
+ &endpoint_dir,
+ &endpoint,
+ return_error,
+ vec![Device {
+ id: device_id.to_string(),
+ properties: HashMap::new(),
+ mounts: Vec::default(),
+ device_specs: Vec::default(),
+ }],
)
.await;
- let shared = false;
- let periodic_dicovery = PeriodicDiscovery {
- config_name: config_name.clone(),
- config_uid: config.metadata.uid.as_ref().unwrap().clone(),
- config_namespace: config.metadata.namespace.as_ref().unwrap().clone(),
- config_spec: config.spec.clone(),
- config_protocol: config.spec.protocol.clone(),
- instance_map: instance_map.clone(),
- };
- periodic_dicovery
- .update_connectivity_status(&mock, ¤tly_visible_instances, shared)
+ // Make sure registration server has started
+ akri_shared::uds::unix_stream::try_connect(&endpoint)
.await
.unwrap();
- let unwrapped_instance_map = instance_map.lock().await.clone();
- for (_, instance_info) in unwrapped_instance_map {
- assert_eq!(
- instance_info.connectivity_status,
- ConnectivityStatus::Online
- );
- }
- }
- /// Checks the termination case for when an unshared instance is still offline upon the second periodic discovery
- /// Must be run independently since writing "OFFLINE" to DEBUG_ECHO_AVAILABILITY_CHECK_PATH in order to emulate
- /// offline devices can clobber other tests run in parallel that are looking for online devices.
- /// Run with: cargo test -- test_do_periodic_discovery --ignored
- #[tokio::test]
- #[ignore]
- async fn test_do_periodic_discovery() {
- let _ = env_logger::builder().is_test(true).try_init();
- // Set env vars
- env::set_var("AGENT_NODE_NAME", "node-a");
- env::set_var("ENABLE_DEBUG_ECHO", "yes");
- // Make each get_instances check return an empty list of instances
- let path_to_config = "../test/json/config-a.json";
- let dcc_json = fs::read_to_string(path_to_config).expect("Unable to read file");
- let config: KubeAkriConfig = serde_json::from_str(&dcc_json).unwrap();
- let config_name = config.metadata.name.clone();
- let config_uid = config.metadata.uid.as_ref().unwrap().clone();
- let config_namespace = config.metadata.namespace.as_ref().unwrap().clone();
- let protocol = config.spec.protocol.clone();
- let mut visible_discovery_results = Vec::new();
- let mut list_and_watch_message_receivers = Vec::new();
- let (mut watch_periph_tx, watch_periph_rx) = mpsc::channel(2);
- let (finished_watching_tx, mut finished_watching_rx) = broadcast::channel(2);
- let mut mock = MockKubeImpl::new();
+ // Add Discovery Handler to map
+ let dh_name = "debugEcho";
+ let discovery_handler_map = Arc::new(std::sync::Mutex::new(HashMap::new()));
+ add_discovery_handler_to_map(dh_name, &dh_endpoint, false, discovery_handler_map.clone());
- // Set ConnectivityStatus of all instances in InstanceMap initially to Offline
- let instance_map: InstanceMap = build_instance_map(
- &config,
- &mut visible_discovery_results,
- &mut list_and_watch_message_receivers,
- ConnectivityStatus::Offline(Instant::now()),
+ // Set up, run, and test handle_config_add
+ // Discovery Handler should create an instance and be marked as Active
+ let path_to_config = "../test/yaml/config-a.yaml";
+ let config_yaml = fs::read_to_string(path_to_config).expect("Unable to read file");
+ let config: KubeAkriConfig = serde_yaml::from_str(&config_yaml).unwrap();
+ let config_name = config.metadata.name.clone();
+ let config_map: ConfigMap = Arc::new(Mutex::new(HashMap::new()));
+ let first_add_handle = run_and_test_handle_config_add(
+ discovery_handler_map.clone(),
+ config_map.clone(),
+ config.clone(),
+ &dh_endpoint,
+ dh_name,
)
.await;
- //
- // Assert that when an unshared instance is already offline it is terminated
- //
- mock.expect_delete_instance()
- .times(2)
- .returning(move |_, _| Ok(()));
- let instance_map_clone = instance_map.clone();
- // Change instances to be offline
- fs::write(DEBUG_ECHO_AVAILABILITY_CHECK_PATH, OFFLINE).unwrap();
- tokio::spawn(async move {
- let periodic_dicovery = PeriodicDiscovery {
- config_name: config_name.clone(),
- config_uid: config_uid.clone(),
- config_namespace: config_namespace.clone(),
- config_spec: config.spec,
- config_protocol: protocol,
- instance_map: instance_map_clone,
- };
- let device_plugin_temp_dir =
- Builder::new().prefix("device-plugins-").tempdir().unwrap();
- let device_plugin_temp_dir_path = device_plugin_temp_dir.path().to_str().unwrap();
- periodic_dicovery
- .do_periodic_discovery(
- &mock,
- watch_periph_rx,
- finished_watching_tx,
- device_plugin_temp_dir_path,
- )
- .await
- .unwrap();
- });
- let mut tasks = Vec::new();
- for mut receiver in list_and_watch_message_receivers {
- tasks.push(tokio::spawn(async move {
- assert_eq!(
- receiver.recv().await.unwrap(),
- device_plugin_service::ListAndWatchMessageKind::End
- );
- }));
- }
- futures::future::join_all(tasks).await;
+ let config_delete_config = config.clone();
+ let config_delete_config_map = config_map.clone();
+ handle_config_delete(
+ &MockKubeInterface::new(),
+ &config_delete_config,
+ config_delete_config_map.clone(),
+ )
+ .await
+ .unwrap();
- // Assert that all instances have been removed from the instance map
- assert_eq!(instance_map.lock().await.len(), 0);
+ // Assert that config is removed from map after it has been deleted
+ assert!(!config_delete_config_map
+ .lock()
+ .await
+ .contains_key(&config_name));
+
+ // Assert that Discovery Handler is marked as Waiting
+ check_discovery_handler_status(
+ discovery_handler_map.clone(),
+ dh_name,
+ &dh_endpoint,
+ DiscoveryHandlerStatus::Waiting,
+ )
+ .await;
- watch_periph_tx.send(()).await.unwrap();
- // Assert that replies saying finished watching
- assert!(finished_watching_rx.recv().await.is_ok());
+ let second_add_handle = run_and_test_handle_config_add(
+ discovery_handler_map.clone(),
+ config_map.clone(),
+ config.clone(),
+ &dh_endpoint,
+ dh_name,
+ )
+ .await;
+
+ // Assert that Discovery Handler is marked as Waiting
+ check_discovery_handler_status(
+ discovery_handler_map.clone(),
+ dh_name,
+ &dh_endpoint,
+ DiscoveryHandlerStatus::Waiting,
+ )
+ .await;
- // Reset file to be online
- fs::write(DEBUG_ECHO_AVAILABILITY_CHECK_PATH, "ONLINE").unwrap();
+ futures::future::join_all(vec![first_add_handle, second_add_handle]).await;
}
}
diff --git a/agent/src/util/constants.rs b/agent/src/util/constants.rs
index 4cf5f6d95..1cfd651b0 100644
--- a/agent/src/util/constants.rs
+++ b/agent/src/util/constants.rs
@@ -1,9 +1,9 @@
-/// For unshared devices, Healthy means the device is discoverable.
-/// For shared devices, Healthy means the device is either unused or used by this node.
+/// For unshared devices, Healthy means the device is discoverable. For shared devices, Healthy means the device is
+/// either unused or used by this node.
pub const HEALTHY: &str = "Healthy";
-/// For unshared devices, Unhealthy means the device is not discoverable.
-/// For shared devices, UnHealthy means that the device shared and used already by another node.
+/// For unshared devices, Unhealthy means the device is not discoverable. For shared devices, Unhealthy means that the
+/// device shared and used already by another node.
pub const UNHEALTHY: &str = "Unhealthy";
/// Current version of the API supported by kubelet.
@@ -18,14 +18,62 @@ pub const KUBELET_SOCKET: &str = "/var/lib/kubelet/device-plugins/kubelet.sock";
/// Maximum length of time `list_and_watch` will sleep before sending kubelet another list of virtual devices
pub const LIST_AND_WATCH_SLEEP_SECS: u64 = 60;
-/// Length of time to sleep between instance discovery checks
-pub const DISCOVERY_DELAY_SECS: u64 = 10;
-
/// Length of time a shared instance can be offline before it's `DevicePluginService` is shutdown.
pub const SHARED_INSTANCE_OFFLINE_GRACE_PERIOD_SECS: u64 = 300;
/// Length of time to sleep between slot reconciliation checks
pub const SLOT_RECONCILIATION_CHECK_DELAY_SECS: u64 = 10;
-/// Length of time a slot can be unused before slot reconciliation relaims it
+/// Length of time a slot can be unused before slot reconciliation reclaims it
pub const SLOT_RECONCILIATION_SLOT_GRACE_PERIOD_SECS: u64 = 300;
+
+/// Label of environment variable that, when set, enables the embedded debug echo discovery handler
+pub const ENABLE_DEBUG_ECHO_LABEL: &str = "ENABLE_DEBUG_ECHO";
+
+/// Maximum amount of time allowed to pass without being able to connect to a discovery handler without it being removed
+/// from the map of registered Discovery Handlers.
+pub const DISCOVERY_HANDLER_OFFLINE_GRACE_PERIOD_SECS: u64 = 300;
+
+/// Capacity of channel over which `DevicePluginService::list_and_watch` sends updates to kubelet about "virtual" device
+/// health of an instance. The kubelet Device Plugin manager should receive each message instantly; however, providing
+/// some buffer in case.
+pub const KUBELET_UPDATE_CHANNEL_CAPACITY: usize = 4;
+
+/// Capacity of channel over which the Agent Registration updates `DiscoveryOperators` when new `DiscoveryHandlers`
+/// register. Tokio does not provide an unbounded broadcast channel in order to prevent the channel from growing
+/// infinitely due to a "slow receiver". It is hard to determine an appropriate channel size, since the number of
+/// `DiscoveryOperator` receivers (equivalent to number of applied Akri Configurations) and the frequency of sends
+/// (equivalent to the number of registering `DiscoveryHandlers`) are unpredictable. Therefore, a large size is chosen
+/// out of caution.
+pub const NEW_DISCOVERY_HANDLER_CHANNEL_CAPACITY: usize = 15;
+
+/// Capacity of channel over which the `DevicePluginService::list_and_watch` receives messages to
+/// `ListAndWatchMessageKind::Continue` (prematurely send updates to kubelet) or `ListAndWatchMessageKind::End`
+/// (terminate itself). `list_and_watch` receives messages asynchronously from `DevicePluginService.allocate`,
+/// `DiscoveryOperator.update_connectivity_status`, and `handle_config_delete`. Messages are sent as a response to a
+/// variety of events, such as an Instance going offline/online, a Configuration being deleted, or a slot being
+/// requested via allocate that is already taken, making it hard to determine the appropriate size of the channel. If a
+/// new message is put in the channel after capacity is already met, the oldest message is dropped, dropping a
+/// `ListAndWatchMessageKind::End` would likely be unrecoverable. Tokio does not provide an unbounded broadcast channel
+/// in order to prevent the channel from growing infinitely due to a "slow receiver", so a large channel size is chosen
+/// out of caution.
+pub const LIST_AND_WATCH_MESSAGE_CHANNEL_CAPACITY: usize = 15;
+
+/// Capacity of channel over which a `DevicePluginService` receives a shutdown signal. This is either sent by
+/// `DevicePluginBuilder::register` or `DevicePluginService::list_and_watch`. Capacity is set to meet worst case
+/// scenario in which they both send messages at the same time.
+pub const DEVICE_PLUGIN_SERVER_ENDER_CHANNEL_CAPACITY: usize = 2;
+
+/// Capacity of channel over which a `DiscoveryOperator` is notified to stop discovery for its Configuration. This
+/// signals it to tell each of its subtasks to stop discovery. Message is only sent once, upon Configuration deletion.
+pub const DISCOVERY_OPERATOR_STOP_DISCOVERY_CHANNEL_CAPACITY: usize = 1;
+
+/// Capacity of channel over which a DiscoveryOperator signals that it has stopped discovery and a Configuration's
+/// Instances and associated `DevicePluginServices` can safely be deleted/terminated. There is only one sender
+/// (`DiscoveryOperator`) who only sends a message once.
+pub const DISCOVERY_OPERATOR_FINISHED_DISCOVERY_CHANNEL_CAPACITY: usize = 1;
+
+/// Capacity of channel over which `DiscoveryOperator` is notified to stop (trying to make) a connection with a
+/// `DiscoveryHandler`. Sent once by the Agent Registration service when a `DiscoveryHandler` re-registers with a different
+/// registration request (edge case).
+pub const CLOSE_DISCOVERY_HANDLER_CONNECTION_CHANNEL_CAPACITY: usize = 1;
diff --git a/agent/src/util/device_plugin_builder.rs b/agent/src/util/device_plugin_builder.rs
new file mode 100644
index 000000000..ed31124b2
--- /dev/null
+++ b/agent/src/util/device_plugin_builder.rs
@@ -0,0 +1,361 @@
+use super::{
+ constants::{
+ DEVICE_PLUGIN_PATH, DEVICE_PLUGIN_SERVER_ENDER_CHANNEL_CAPACITY, K8S_DEVICE_PLUGIN_VERSION,
+ KUBELET_SOCKET, LIST_AND_WATCH_MESSAGE_CHANNEL_CAPACITY,
+ },
+ device_plugin_service::{DevicePluginService, InstanceMap},
+ v1beta1,
+ v1beta1::{device_plugin_server::DevicePluginServer, registration_client, DevicePluginOptions},
+};
+use akri_discovery_utils::discovery::v0::Device;
+use akri_shared::{
+ akri::{configuration::KubeAkriConfig, AKRI_PREFIX},
+ uds::unix_stream,
+};
+use async_trait::async_trait;
+use futures::stream::TryStreamExt;
+use log::{info, trace};
+#[cfg(test)]
+use mockall::{automock, predicate::*};
+use std::{convert::TryFrom, env, path::Path, time::SystemTime};
+use tokio::{
+ net::UnixListener,
+ net::UnixStream,
+ sync::{broadcast, mpsc},
+ task,
+};
+use tonic::transport::{Endpoint, Server, Uri};
+use tower::service_fn;
+
+#[cfg_attr(test, automock)]
+#[async_trait]
+pub trait DevicePluginBuilderInterface: Send + Sync {
+ async fn build_device_plugin(
+ &self,
+ instance_name: String,
+ config: &KubeAkriConfig,
+ shared: bool,
+ instance_map: InstanceMap,
+ device: Device,
+ ) -> Result<(), Box>;
+
+ async fn serve(
+ &self,
+ device_plugin_service: DevicePluginService,
+ socket_path: String,
+ server_ender_receiver: mpsc::Receiver<()>,
+ ) -> Result<(), Box>;
+
+ async fn register(
+ &self,
+ capability_id: &str,
+ socket_name: &str,
+ instance_name: &str,
+ mut server_ender_sender: mpsc::Sender<()>,
+ kubelet_socket: &str,
+ ) -> Result<(), Box>;
+}
+
+/// For each Instance, builds a Device Plugin, registers it with the kubelet, and serves it over UDS.
+pub struct DevicePluginBuilder {}
+
+#[async_trait]
+impl DevicePluginBuilderInterface for DevicePluginBuilder {
+ /// This creates a new DevicePluginService for an instance and registers it with the kubelet
+ async fn build_device_plugin(
+ &self,
+ instance_name: String,
+ config: &KubeAkriConfig,
+ shared: bool,
+ instance_map: InstanceMap,
+ device: Device,
+ ) -> Result<(), Box> {
+ info!("build_device_plugin - entered for device {}", instance_name);
+ let capability_id: String = format!("{}/{}", AKRI_PREFIX, instance_name);
+ let unique_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
+ let device_endpoint: String = format!("{}-{}.sock", instance_name, unique_time.as_secs());
+ let socket_path: String = Path::new(DEVICE_PLUGIN_PATH)
+ .join(device_endpoint.clone())
+ .to_str()
+ .unwrap()
+ .to_string();
+ let (list_and_watch_message_sender, _) =
+ broadcast::channel(LIST_AND_WATCH_MESSAGE_CHANNEL_CAPACITY);
+ let (server_ender_sender, server_ender_receiver) =
+ mpsc::channel(DEVICE_PLUGIN_SERVER_ENDER_CHANNEL_CAPACITY);
+ let device_plugin_service = DevicePluginService {
+ instance_name: instance_name.clone(),
+ endpoint: device_endpoint.clone(),
+ config: config.spec.clone(),
+ config_name: config.metadata.name.clone(),
+ config_uid: config.metadata.uid.as_ref().unwrap().clone(),
+ config_namespace: config.metadata.namespace.as_ref().unwrap().clone(),
+ shared,
+ node_name: env::var("AGENT_NODE_NAME")?,
+ instance_map,
+ list_and_watch_message_sender,
+ server_ender_sender: server_ender_sender.clone(),
+ device,
+ };
+
+ self.serve(
+ device_plugin_service,
+ socket_path.clone(),
+ server_ender_receiver,
+ )
+ .await?;
+
+ self.register(
+ &capability_id,
+ &device_endpoint,
+ &instance_name,
+ server_ender_sender,
+ KUBELET_SOCKET,
+ )
+ .await?;
+
+ Ok(())
+ }
+
+ // This starts a DevicePluginServer
+ async fn serve(
+ &self,
+ device_plugin_service: DevicePluginService,
+ socket_path: String,
+ server_ender_receiver: mpsc::Receiver<()>,
+ ) -> Result<(), Box> {
+ info!(
+ "serve - creating a device plugin server that will listen at: {}",
+ socket_path
+ );
+ tokio::fs::create_dir_all(Path::new(&socket_path[..]).parent().unwrap())
+ .await
+ .expect("Failed to create dir at socket path");
+ let mut uds =
+ UnixListener::bind(socket_path.clone()).expect("Failed to bind to socket path");
+ let service = DevicePluginServer::new(device_plugin_service);
+ let socket_path_to_delete = socket_path.clone();
+ task::spawn(async move {
+ Server::builder()
+ .add_service(service)
+ .serve_with_incoming_shutdown(
+ uds.incoming().map_ok(unix_stream::UnixStream),
+ shutdown_signal(server_ender_receiver),
+ )
+ .await
+ .unwrap();
+ trace!(
+ "serve - gracefully shutdown ... deleting socket {}",
+ socket_path_to_delete
+ );
+ // Socket may already be deleted in the case of the kubelet restart
+ std::fs::remove_file(socket_path_to_delete).unwrap_or(());
+ });
+
+ akri_shared::uds::unix_stream::try_connect(&socket_path).await?;
+ Ok(())
+ }
+
+ /// This registers DevicePlugin with the kubelet.
+ /// During registration, the device plugin must send
+ /// (1) name of unix socket,
+ /// (2) Device-Plugin API it was built against (v1beta1),
+ /// (3) resource name akri.sh/device_id.
+ /// If registration request to the kubelet fails, terminates DevicePluginService.
+ async fn register(
+ &self,
+ capability_id: &str,
+ socket_name: &str,
+ instance_name: &str,
+ mut server_ender_sender: mpsc::Sender<()>,
+ kubelet_socket: &str,
+ ) -> Result<(), Box> {
+ info!(
+ "register - entered for Instance {} and socket_name: {}",
+ capability_id, socket_name
+ );
+ let op = DevicePluginOptions {
+ pre_start_required: false,
+ };
+
+ // We will ignore this dummy uri because UDS does not use it.
+ let kubelet_socket_closure = kubelet_socket.to_string();
+ let channel = Endpoint::try_from("dummy://[::]:50051")?
+ .connect_with_connector(service_fn(move |_: Uri| {
+ UnixStream::connect(kubelet_socket_closure.clone())
+ }))
+ .await?;
+ let mut registration_client = registration_client::RegistrationClient::new(channel);
+
+ let register_request = tonic::Request::new(v1beta1::RegisterRequest {
+ version: K8S_DEVICE_PLUGIN_VERSION.into(),
+ endpoint: socket_name.to_string(),
+ resource_name: capability_id.to_string(),
+ options: Some(op),
+ });
+ trace!(
+ "register - before call to register with the kubelet at socket {}",
+ kubelet_socket
+ );
+
+ // If fail to register with the kubelet, terminate device plugin
+ if registration_client
+ .register(register_request)
+ .await
+ .is_err()
+ {
+ trace!(
+ "register - failed to register Instance {} with the kubelet ... terminating device plugin",
+ instance_name
+ );
+ server_ender_sender.send(()).await?;
+ }
+ Ok(())
+ }
+}
+
+/// This acts as a signal future to gracefully shutdown DevicePluginServer upon its completion.
+/// Ends when it receives message from `list_and_watch`.
+async fn shutdown_signal(mut server_ender_receiver: mpsc::Receiver<()>) {
+ match server_ender_receiver.recv().await {
+ Some(_) => trace!(
+ "shutdown_signal - received signal ... device plugin service gracefully shutting down"
+ ),
+ None => trace!("shutdown_signal - connection to server_ender_sender closed ... error"),
+ }
+}
+
+#[cfg(test)]
+pub mod tests {
+ use super::super::v1beta1::{
+ registration_server::{Registration, RegistrationServer},
+ Empty, RegisterRequest,
+ };
+ use super::*;
+ use tempfile::Builder;
+
+ struct MockRegistration {
+ pub return_error: bool,
+ }
+
+ // Mock implementation of kubelet's registration service for tests.
+ // Can be configured with its `return_error` field to return an error.
+ #[async_trait]
+ impl Registration for MockRegistration {
+ async fn register(
+ &self,
+ _request: tonic::Request,
+ ) -> Result, tonic::Status> {
+ if self.return_error {
+ Err(tonic::Status::invalid_argument(
+ "mock discovery handler error",
+ ))
+ } else {
+ Ok(tonic::Response::new(Empty {}))
+ }
+ }
+ }
+
+ #[tokio::test]
+ async fn test_register() {
+ let device_plugins_dirs = Builder::new().prefix("device-plugins").tempdir().unwrap();
+ let kubelet_socket = device_plugins_dirs
+ .path()
+ .join("kubelet.sock")
+ .to_str()
+ .unwrap()
+ .to_string();
+
+ // Start kubelet registration server
+ let mut uds =
+ UnixListener::bind(kubelet_socket.clone()).expect("Failed to bind to socket path");
+
+ let registration = MockRegistration {
+ return_error: false,
+ };
+ let service = RegistrationServer::new(registration);
+ task::spawn(async move {
+ Server::builder()
+ .add_service(service)
+ .serve_with_incoming(uds.incoming().map_ok(unix_stream::UnixStream))
+ .await
+ .unwrap();
+ });
+
+ // Make sure registration server has started
+ akri_shared::uds::unix_stream::try_connect(&kubelet_socket)
+ .await
+ .unwrap();
+
+ let device_plugin_builder = DevicePluginBuilder {};
+ let (server_ender_sender, _) = mpsc::channel(1);
+ // Test successful registration
+ assert!(device_plugin_builder
+ .register(
+ "random_instance_id",
+ "socket.sock",
+ "random_instance",
+ server_ender_sender,
+ &kubelet_socket
+ )
+ .await
+ .is_ok());
+ }
+
+ #[tokio::test]
+ async fn test_register_error() {
+ let device_plugin_builder = DevicePluginBuilder {};
+ let (server_ender_sender, mut server_ender_receiver) = mpsc::channel(1);
+ let device_plugins_dirs = Builder::new().prefix("device-plugins").tempdir().unwrap();
+ let kubelet_socket = device_plugins_dirs
+ .path()
+ .join("kubelet.sock")
+ .to_str()
+ .unwrap()
+ .to_string();
+
+ // Try to register when no registration service exists
+ assert!(device_plugin_builder
+ .register(
+ "random_instance_id",
+ "socket.sock",
+ "random_instance",
+ server_ender_sender.clone(),
+ &kubelet_socket
+ )
+ .await
+ .is_err());
+
+ // Start kubelet registration server
+ let mut uds =
+ UnixListener::bind(kubelet_socket.clone()).expect("Failed to bind to socket path");
+ let registration = MockRegistration { return_error: true };
+ let service = RegistrationServer::new(registration);
+ task::spawn(async move {
+ Server::builder()
+ .add_service(service)
+ .serve_with_incoming(uds.incoming().map_ok(unix_stream::UnixStream))
+ .await
+ .unwrap();
+ });
+
+ // Make sure registration server has started
+ akri_shared::uds::unix_stream::try_connect(&kubelet_socket)
+ .await
+ .unwrap();
+
+ // Test that when registration fails, no error is thrown but the DevicePluginService is signaled to shutdown
+ assert!(device_plugin_builder
+ .register(
+ "random_instance_id",
+ "socket.sock",
+ "random_instance",
+ server_ender_sender,
+ &kubelet_socket
+ )
+ .await
+ .is_ok());
+ // Make sure DevicePluginService is signaled to shutdown
+ server_ender_receiver.recv().await.unwrap();
+ }
+}
diff --git a/agent/src/util/device_plugin_service.rs b/agent/src/util/device_plugin_service.rs
index 6d9440f21..c0566f702 100644
--- a/agent/src/util/device_plugin_service.rs
+++ b/agent/src/util/device_plugin_service.rs
@@ -1,44 +1,34 @@
use super::constants::{
- HEALTHY, K8S_DEVICE_PLUGIN_VERSION, KUBELET_SOCKET, LIST_AND_WATCH_SLEEP_SECS, UNHEALTHY,
+ HEALTHY, KUBELET_UPDATE_CHANNEL_CAPACITY, LIST_AND_WATCH_SLEEP_SECS, UNHEALTHY,
};
use super::v1beta1;
use super::v1beta1::{
- device_plugin_server::{DevicePlugin, DevicePluginServer},
- registration_client, AllocateRequest, AllocateResponse, DevicePluginOptions, Empty,
- ListAndWatchResponse, PreStartContainerRequest, PreStartContainerResponse,
+ device_plugin_server::DevicePlugin, AllocateRequest, AllocateResponse, DevicePluginOptions,
+ DeviceSpec, Empty, ListAndWatchResponse, Mount, PreStartContainerRequest,
+ PreStartContainerResponse,
};
+use akri_discovery_utils::discovery::v0::Device;
use akri_shared::{
akri::{
- configuration::{Configuration, ProtocolHandler},
+ configuration::Configuration,
instance::Instance,
retry::{random_delay, MAX_INSTANCE_UPDATE_TRIES},
- AKRI_PREFIX, AKRI_SLOT_ANNOTATION_NAME,
+ AKRI_SLOT_ANNOTATION_NAME,
},
k8s,
k8s::KubeInterface,
};
-use futures::stream::TryStreamExt;
use log::{error, info, trace};
-use std::{
- collections::HashMap,
- convert::TryFrom,
- env,
- path::Path,
- sync::Arc,
- time::{Duration, Instant, SystemTime, UNIX_EPOCH},
-};
+#[cfg(test)]
+use mock_instant::Instant;
+#[cfg(not(test))]
+use std::time::Instant;
+use std::{collections::HashMap, sync::Arc, time::Duration};
use tokio::{
- net::UnixListener,
- net::UnixStream,
sync::{broadcast, mpsc, Mutex},
- task,
- time::{delay_for, timeout},
-};
-use tonic::{
- transport::{Endpoint, Server, Uri},
- Code, Request, Response, Status,
+ time::timeout,
};
-use tower::service_fn;
+use tonic::{Code, Request, Response, Status};
/// Message sent in channel to `list_and_watch`.
/// Dictates what action `list_and_watch` should take upon being awoken.
@@ -50,9 +40,9 @@ pub enum ListAndWatchMessageKind {
End,
}
-/// Describes the discoverability of an instance for this node
+/// Describes whether an instance was discovered or the time at which it was no longer discovered.
#[derive(PartialEq, Debug, Clone)]
-pub enum ConnectivityStatus {
+pub enum InstanceConnectivityStatus {
/// Was discovered
Online,
/// Could not be discovered. Instant contains time at which it was no longer discovered.
@@ -64,8 +54,8 @@ pub enum ConnectivityStatus {
pub struct InstanceInfo {
/// Sender to tell `list_and_watch` to either prematurely continue looping or end
pub list_and_watch_message_sender: broadcast::Sender,
- /// Instance's `ConnectivityStatus`
- pub connectivity_status: ConnectivityStatus,
+ /// Instance's `InstanceConnectivityStatus`
+ pub connectivity_status: InstanceConnectivityStatus,
}
pub type InstanceMap = Arc>>;
@@ -81,32 +71,34 @@ pub type InstanceMap = Arc>>;
#[derive(Clone)]
pub struct DevicePluginService {
/// Instance CRD name
- instance_name: String,
+ pub instance_name: String,
/// Socket endpoint
- endpoint: String,
+ pub endpoint: String,
/// Instance's Configuration
- config: Configuration,
+ pub config: Configuration,
/// Name of Instance's Configuration CRD
- config_name: String,
+ pub config_name: String,
/// UID of Instance's Configuration CRD
- config_uid: String,
+ pub config_uid: String,
/// Namespace of Instance's Configuration CRD
- config_namespace: String,
+ pub config_namespace: String,
/// Instance is [not]shared
- shared: bool,
+ pub shared: bool,
/// Hostname of node this Device Plugin is running on
- node_name: String,
- /// Information that must be communicated with broker. Stored in Instance CRD as metadata.
- instance_properties: HashMap,
+ pub node_name: String,
/// Map of all Instances that have the same Configuration CRD as this one
- instance_map: InstanceMap,
+ pub instance_map: InstanceMap,
/// Receiver for list_and_watch continue or end messages
/// Note: since the tonic grpc generated list_and_watch definition takes in &self,
/// using broadcast sender instead of mpsc receiver
/// Can clone broadcast sender and subscribe receiver to use in spawned thread in list_and_watch
- list_and_watch_message_sender: broadcast::Sender,
+ pub list_and_watch_message_sender: broadcast::Sender,
/// Upon send, terminates function that acts as the shutdown signal for this service
- server_ender_sender: mpsc::Sender<()>,
+ pub server_ender_sender: mpsc::Sender<()>,
+ /// Device that the instance represents.
+ /// Contains information about environment variables and volumes that should be mounted
+ /// into requesting Pods.
+ pub device: Device,
}
#[tonic::async_trait]
@@ -144,7 +136,8 @@ impl DevicePlugin for DevicePluginService {
let mut list_and_watch_message_receiver = self.list_and_watch_message_sender.subscribe();
// Create a channel that list_and_watch can periodically send updates to kubelet on
- let (mut kubelet_update_sender, kubelet_update_receiver) = mpsc::channel(4);
+ let (mut kubelet_update_sender, kubelet_update_receiver) =
+ mpsc::channel(KUBELET_UPDATE_CHANNEL_CAPACITY);
// Spawn thread so can send kubelet the receiving end of the channel to listen on
tokio::spawn(async move {
let mut keep_looping = true;
@@ -323,10 +316,12 @@ impl DevicePluginService {
}
// Successfully reserved device_usage_slot[s] for this node.
// Add response to list of responses
+ let broker_properties =
+ get_all_broker_properties(&self.config.broker_properties, &self.device.properties);
let response = build_container_allocate_response(
+ broker_properties,
akri_annotations,
- &self.instance_properties,
- &self.config.protocol,
+ &self.device,
);
container_responses.push(response);
}
@@ -357,7 +352,7 @@ fn get_slot_value(
instance: &Instance,
) -> Result {
if let Some(allocated_node) = instance.device_usage.get(device_usage_id) {
- if allocated_node == "" {
+ if allocated_node.is_empty() {
Ok(node_name.to_string())
} else if allocated_node == node_name {
Ok("".to_string())
@@ -445,36 +440,40 @@ async fn try_update_instance_device_usage(
Ok(())
}
-/// This sets the volume mounts and environment variables according to the instance's protocol.
+/// This sets the volume mounts and environment variables according to the instance's `DiscoveryHandler`.
fn build_container_allocate_response(
+ broker_properties: HashMap,
annotations: HashMap,
- instance_properties: &HashMap,
- protocol: &ProtocolHandler,
+ device: &Device,
) -> v1beta1::ContainerAllocateResponse {
- let mut mounts: Vec = Vec::new();
-
- // Set mounts according to protocol
- match protocol {
- ProtocolHandler::udev(_handler_config) => {
- trace!("get_volumes_and_mounts - setting volumes and mounts for udev protocol");
- mounts = instance_properties
- .iter()
- .map(|(_id, devpath)| v1beta1::Mount {
- container_path: devpath.clone(),
- host_path: devpath.clone(),
- read_only: true,
- })
- .collect();
- }
- _ => trace!("get_volumes_and_mounts - no mounts or volumes required by this protocol"),
- }
+ // Cast v0 discovery Mount and DeviceSpec types to v1beta1 DevicePlugin types
+ let mounts: Vec = device
+ .mounts
+ .clone()
+ .into_iter()
+ .map(|mount| Mount {
+ container_path: mount.container_path,
+ host_path: mount.host_path,
+ read_only: mount.read_only,
+ })
+ .collect();
+ let device_specs: Vec = device
+ .device_specs
+ .clone()
+ .into_iter()
+ .map(|device_spec| DeviceSpec {
+ container_path: device_spec.container_path,
+ host_path: device_spec.host_path,
+ permissions: device_spec.permissions,
+ })
+ .collect();
- // Create response, setting environment variables to be an instance's properties (specified by protocol)
+ // Create response, setting environment variables to be an instance's properties.
v1beta1::ContainerAllocateResponse {
annotations,
mounts,
- envs: instance_properties.clone(),
- ..Default::default()
+ devices: device_specs,
+ envs: broker_properties,
}
}
@@ -506,8 +505,10 @@ async fn try_create_instance(
shared: dps.shared,
nodes: vec![dps.node_name.clone()],
device_usage,
- metadata: dps.instance_properties.clone(),
- rbac: "rbac".to_string(),
+ broker_properties: get_all_broker_properties(
+ &dps.config.broker_properties,
+ &dps.device.properties,
+ ),
};
// Try up to MAX_INSTANCE_UPDATE_TRIES to create or update instance, breaking on success
@@ -588,7 +589,7 @@ async fn try_create_instance(
dps.instance_name.clone(),
InstanceInfo {
list_and_watch_message_sender: dps.list_and_watch_message_sender.clone(),
- connectivity_status: ConnectivityStatus::Online,
+ connectivity_status: InstanceConnectivityStatus::Online,
},
);
@@ -627,7 +628,7 @@ async fn build_list_and_watch_response(
.get(&dps.instance_name)
.unwrap()
.connectivity_status
- != ConnectivityStatus::Online
+ != InstanceConnectivityStatus::Online
{
trace!("build_list_and_watch_response - device for Instance {} is offline ... returning unhealthy devices", dps.instance_name);
return Ok(build_unhealthy_virtual_devices(
@@ -689,12 +690,12 @@ fn build_virtual_devices(
let mut devices: Vec = Vec::new();
for (device_name, allocated_node) in device_usage {
// Throw error if unshared resource is reserved by another node
- if !shared && allocated_node != "" && allocated_node != node_name {
+ if !shared && !allocated_node.is_empty() && allocated_node != node_name {
panic!("build_virtual_devices - unshared device reserved by a different node");
}
// Advertise the device as Unhealthy if it is
// USED by !this_node && SHARED
- let unhealthy = shared && allocated_node != "" && allocated_node != node_name;
+ let unhealthy = shared && !allocated_node.is_empty() && allocated_node != node_name;
let health = if unhealthy {
UNHEALTHY.to_string()
} else {
@@ -721,7 +722,7 @@ pub async fn terminate_device_plugin_service(
instance_map: InstanceMap,
) -> Result<(), Box> {
let mut instance_map = instance_map.lock().await;
- trace!(
+ info!(
"terminate_device_plugin_service -- forcing list_and_watch to end for Instance {}",
instance_name
);
@@ -740,198 +741,6 @@ pub async fn terminate_device_plugin_service(
Ok(())
}
-/// This creates a new DevicePluginService for an instance and registers it with kubelet
-pub async fn build_device_plugin(
- instance_name: String,
- config_name: String,
- config_uid: String,
- config_namespace: String,
- config: Configuration,
- shared: bool,
- instance_properties: HashMap,
- instance_map: InstanceMap,
- device_plugin_path: &str,
-) -> Result<(), Box> {
- info!("build_device_plugin - entered for device {}", instance_name);
- let capability_id: String = format!("{}/{}", AKRI_PREFIX, instance_name);
- let unique_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
- let device_endpoint: String = format!("{}-{}.sock", instance_name, unique_time.as_secs());
- let socket_path: String = Path::new(device_plugin_path)
- .join(device_endpoint.clone())
- .to_str()
- .unwrap()
- .to_string();
- // Channel capacity set to 6 because 3 possible senders (allocate, update_connectivity_status, and handle_config_delete)
- // and and receiver only periodically checks channel
- let (list_and_watch_message_sender, _) = broadcast::channel(6);
- // Channel capacity set to 2 because worst case both register and list_and_watch send messages at same time and receiver is always listening
- let (server_ender_sender, server_ender_receiver) = mpsc::channel(2);
- let device_plugin_service = DevicePluginService {
- instance_name: instance_name.clone(),
- endpoint: device_endpoint.clone(),
- config,
- config_name: config_name.clone(),
- config_uid: config_uid.clone(),
- config_namespace: config_namespace.clone(),
- shared,
- node_name: env::var("AGENT_NODE_NAME")?,
- instance_properties,
- instance_map: instance_map.clone(),
- list_and_watch_message_sender: list_and_watch_message_sender.clone(),
- server_ender_sender: server_ender_sender.clone(),
- };
-
- serve(
- device_plugin_service,
- socket_path.clone(),
- server_ender_receiver,
- )
- .await?;
-
- register(
- capability_id,
- device_endpoint,
- &instance_name,
- server_ender_sender,
- )
- .await?;
-
- Ok(())
-}
-
-/// This acts as a signal future to gracefully shutdown DevicePluginServer upon its completion.
-/// Ends when it receives message from `list_and_watch`.
-async fn shutdown_signal(mut server_ender_receiver: mpsc::Receiver<()>) {
- match server_ender_receiver.recv().await {
- Some(_) => trace!(
- "shutdown_signal - received signal ... device plugin service gracefully shutting down"
- ),
- None => trace!("shutdown_signal - connection to server_ender_sender closed ... error"),
- }
-}
-
-// This serves DevicePluginServer
-async fn serve(
- device_plugin_service: DevicePluginService,
- socket_path: String,
- server_ender_receiver: mpsc::Receiver<()>,
-) -> Result<(), Box> {
- info!(
- "serve - creating a device plugin server that will listen at: {}",
- socket_path
- );
- tokio::fs::create_dir_all(Path::new(&socket_path[..]).parent().unwrap())
- .await
- .expect("Failed to create dir at socket path");
- let mut uds = UnixListener::bind(socket_path.clone()).expect("Failed to bind to socket path");
- let service = DevicePluginServer::new(device_plugin_service);
- let socket_path_to_delete = socket_path.clone();
- task::spawn(async move {
- Server::builder()
- .add_service(service)
- .serve_with_incoming_shutdown(
- uds.incoming().map_ok(unix::UnixStream),
- shutdown_signal(server_ender_receiver),
- )
- .await
- .unwrap();
- trace!(
- "serve - gracefully shutdown ... deleting socket {}",
- socket_path_to_delete
- );
- // Socket may already be deleted in the case of kubelet restart
- std::fs::remove_file(socket_path_to_delete).unwrap_or(());
- });
-
- // Test that server is running, trying for at most 10 seconds
- // Similar to grpc.timeout, which is yet to be implemented for tonic
- // See issue: https://github.com/hyperium/tonic/issues/75
- let mut connected = false;
- let start = SystemTime::now()
- .duration_since(UNIX_EPOCH)
- .expect("Time went backwards")
- .as_secs();
- let start_plus_10 = start + 10;
-
- while (SystemTime::now()
- .duration_since(UNIX_EPOCH)
- .expect("Time went backwards")
- .as_secs()
- < start_plus_10)
- && !connected
- {
- let path = socket_path.clone();
- if let Ok(_v) = Endpoint::try_from("lttp://[::]:50051")?
- .connect_with_connector(service_fn(move |_: Uri| UnixStream::connect(path.clone())))
- .await
- {
- connected = true
- } else {
- delay_for(Duration::from_secs(1)).await
- }
- }
-
- if !connected {
- error!(
- "serve - could not connect to Device Plugin server on socket {}",
- socket_path
- );
- }
- Ok(())
-}
-
-/// This registers DevicePlugin with kubelet.
-/// During registration, the device plugin must send
-/// (1) name of unix socket,
-/// (2) Device-Plugin API it was built against (v1beta1),
-/// (3) resource name akri.sh/device_id.
-/// If registration request to kubelet fails, terminates DevicePluginService.
-async fn register(
- capability_id: String,
- socket_name: String,
- instance_name: &str,
- mut server_ender_sender: mpsc::Sender<()>,
-) -> Result<(), Box> {
- info!(
- "register - entered for Instance {} and socket_name: {}",
- capability_id, socket_name
- );
- let op = DevicePluginOptions {
- pre_start_required: false,
- };
-
- // lttp://... is a fake uri that is unused (in service_fn) but necessary for uds connection
- let channel = Endpoint::try_from("lttp://[::]:50051")?
- .connect_with_connector(service_fn(|_: Uri| UnixStream::connect(KUBELET_SOCKET)))
- .await?;
- let mut registration_client = registration_client::RegistrationClient::new(channel);
-
- let register_request = tonic::Request::new(v1beta1::RegisterRequest {
- version: K8S_DEVICE_PLUGIN_VERSION.into(),
- endpoint: socket_name,
- resource_name: capability_id,
- options: Some(op),
- });
- trace!(
- "register - before call to register with Kubelet at socket {}",
- KUBELET_SOCKET
- );
-
- // If fail to register with kubelet, terminate device plugin
- if registration_client
- .register(register_request)
- .await
- .is_err()
- {
- trace!(
- "register - failed to register Instance {} with kubelet ... terminating device plugin",
- instance_name
- );
- server_ender_sender.send(()).await?;
- }
- Ok(())
-}
-
/// This creates an Instance's unique name
pub fn get_device_instance_name(id: &str, config_name: &str) -> String {
format!("{}-{}", config_name, &id)
@@ -939,70 +748,40 @@ pub fn get_device_instance_name(id: &str, config_name: &str) -> String {
.replace("/", "-")
}
-/// Module to enable UDS with tonic grpc.
-/// This is unix only since the underlying UnixStream and UnixListener libraries are unix only.
-#[cfg(unix)]
-mod unix {
- use std::{
- pin::Pin,
- task::{Context, Poll},
- };
-
- use tokio::io::{AsyncRead, AsyncWrite};
- use tonic::transport::server::Connected;
-
- #[derive(Debug)]
- pub struct UnixStream(pub tokio::net::UnixStream);
-
- impl Connected for UnixStream {}
-
- impl AsyncRead for UnixStream {
- fn poll_read(
- mut self: Pin<&mut Self>,
- cx: &mut Context<'_>,
- buf: &mut [u8],
- ) -> Poll> {
- Pin::new(&mut self.0).poll_read(cx, buf)
- }
- }
-
- impl AsyncWrite for UnixStream {
- fn poll_write(
- mut self: Pin<&mut Self>,
- cx: &mut Context<'_>,
- buf: &[u8],
- ) -> Poll> {
- Pin::new(&mut self.0).poll_write(cx, buf)
- }
-
- fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> {
- Pin::new(&mut self.0).poll_flush(cx)
- }
-
- fn poll_shutdown(
- mut self: Pin<&mut Self>,
- cx: &mut Context<'_>,
- ) -> Poll> {
- Pin::new(&mut self.0).poll_shutdown(cx)
- }
- }
+// Aggregate a Configuration and Device's properties so they can be displayed in an Instance and injected into brokers as environment variables.
+pub fn get_all_broker_properties(
+ configuration_properties: &HashMap,
+ device_properties: &HashMap,
+) -> HashMap {
+ configuration_properties
+ .clone()
+ .into_iter()
+ .chain(device_properties.clone())
+ .collect::>()
}
#[cfg(test)]
mod device_plugin_service_tests {
- use super::super::v1beta1::device_plugin_client::DevicePluginClient;
+ use super::super::{
+ device_plugin_builder::{DevicePluginBuilder, DevicePluginBuilderInterface},
+ v1beta1::device_plugin_client::DevicePluginClient,
+ };
use super::*;
use akri_shared::akri::configuration::KubeAkriConfig;
use akri_shared::{
akri::instance::{Instance, KubeAkriInstance},
- k8s::test_kube::MockKubeImpl,
+ k8s::MockKubeInterface,
};
- use mockall::predicate::*;
use std::{
+ convert::TryFrom,
fs,
io::{Error, ErrorKind},
+ time::SystemTime,
};
use tempfile::Builder;
+ use tokio::net::UnixStream;
+ use tonic::transport::{Endpoint, Uri};
+ use tower::service_fn;
enum NodeName {
ThisNode,
@@ -1016,11 +795,11 @@ mod device_plugin_service_tests {
}
fn configure_find_instance(
- mock: &mut MockKubeImpl,
+ mock: &mut MockKubeInterface,
result_file: &'static str,
instance_name: String,
instance_namespace: String,
- device_usage_node: &'static str,
+ device_usage_node: String,
node_name: NodeName,
) {
let instance_name_clone = instance_name.clone();
@@ -1046,14 +825,14 @@ mod device_plugin_service_tests {
}
fn create_device_plugin_service(
- connectivity_status: ConnectivityStatus,
+ connectivity_status: InstanceConnectivityStatus,
add_to_instance_map: bool,
) -> (DevicePluginService, DevicePluginServiceReceivers) {
- let path_to_config = "../test/json/config-a.json";
- let kube_akri_config_json =
+ let path_to_config = "../test/yaml/config-a.yaml";
+ let kube_akri_config_yaml =
fs::read_to_string(path_to_config).expect("Unable to read file");
let kube_akri_config: KubeAkriConfig =
- serde_json::from_str(&kube_akri_config_json).unwrap();
+ serde_yaml::from_str(&kube_akri_config_yaml).unwrap();
let device_instance_name =
get_device_instance_name("b494b6", &kube_akri_config.metadata.name);
let unique_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH);
@@ -1075,7 +854,14 @@ mod device_plugin_service_tests {
map.insert(device_instance_name.clone(), instance_info);
}
let instance_map: InstanceMap = Arc::new(Mutex::new(map));
-
+ let mut properties = HashMap::new();
+ properties.insert("DEVICE_LOCATION_INFO".to_string(), "endpoint".to_string());
+ let device = Device {
+ id: "n/a".to_string(),
+ properties,
+ mounts: Vec::new(),
+ device_specs: Vec::new(),
+ };
let dps = DevicePluginService {
instance_name: device_instance_name,
endpoint: device_endpoint,
@@ -1085,10 +871,10 @@ mod device_plugin_service_tests {
config_namespace: kube_akri_config.metadata.namespace.unwrap(),
shared: false,
node_name: "node-a".to_string(),
- instance_properties: HashMap::new(),
instance_map,
list_and_watch_message_sender,
server_ender_sender,
+ device,
};
(
dps,
@@ -1113,6 +899,16 @@ mod device_plugin_service_tests {
}
}
+ fn get_kube_not_found_error() -> kube::Error {
+ // Mock error thrown when instance not found
+ kube::Error::Api(kube::ErrorResponse {
+ status: "Failure".to_string(),
+ message: "instances.akri.sh \"akri-blah-901a7b\" not found".to_string(),
+ reason: "NotFound".to_string(),
+ code: k8s::ERROR_NOT_FOUND,
+ })
+ }
+
// Tests that instance names are formatted correctly
#[test]
fn test_get_device_instance_name() {
@@ -1128,8 +924,26 @@ mod device_plugin_service_tests {
);
}
+ // Test that a Device and Configuration's properties are aggregated and that
+ // a Device property overwrites a Configuration's.
+ #[test]
+ fn test_get_all_broker_properties() {
+ let mut device_properties = HashMap::new();
+ device_properties.insert("ENDPOINT".to_string(), "123".to_string());
+ device_properties.insert("OVERWRITE".to_string(), "222".to_string());
+ let mut configuration_properties = HashMap::new();
+ configuration_properties.insert("USE HD".to_string(), "true".to_string());
+ configuration_properties.insert("OVERWRITE".to_string(), "111".to_string());
+ let all_properties =
+ get_all_broker_properties(&configuration_properties, &device_properties);
+ assert_eq!(all_properties.len(), 3);
+ assert_eq!(all_properties.get("ENDPOINT").unwrap(), "123");
+ assert_eq!(all_properties.get("USE HD").unwrap(), "true");
+ assert_eq!(all_properties.get("OVERWRITE").unwrap(), "222");
+ }
+
fn configure_find_configuration(
- mock: &mut MockKubeImpl,
+ mock: &mut MockKubeInterface,
config_name: String,
config_namespace: String,
) {
@@ -1139,11 +953,11 @@ mod device_plugin_service_tests {
namespace == config_namespace && name == config_name
})
.returning(move |_, _| {
- let path_to_config = "../test/json/config-a.json";
- let kube_akri_config_json =
+ let path_to_config = "../test/yaml/config-a.yaml";
+ let kube_akri_config_yaml =
fs::read_to_string(path_to_config).expect("Unable to read file");
let kube_akri_config: KubeAkriConfig =
- serde_json::from_str(&kube_akri_config_json).unwrap();
+ serde_yaml::from_str(&kube_akri_config_yaml).unwrap();
Ok(kube_akri_config)
});
}
@@ -1153,8 +967,8 @@ mod device_plugin_service_tests {
async fn test_try_create_instance() {
let _ = env_logger::builder().is_test(true).try_init();
let (device_plugin_service, _device_plugin_service_receivers) =
- create_device_plugin_service(ConnectivityStatus::Online, false);
- let mut mock = MockKubeImpl::new();
+ create_device_plugin_service(InstanceConnectivityStatus::Online, false);
+ let mut mock = MockKubeInterface::new();
configure_find_configuration(
&mut mock,
device_plugin_service.config_name.clone(),
@@ -1169,10 +983,7 @@ mod device_plugin_service_tests {
.withf(move |name: &str, namespace: &str| {
namespace == config_namespace && name == instance_name
})
- .returning(move |_, _| {
- let error = Error::new(ErrorKind::InvalidInput, "Configuration doesn't exist");
- Err(Box::new(error))
- });
+ .returning(move |_, _| Err(get_kube_not_found_error()));
let instance_name = device_plugin_service.instance_name.clone();
let config_namespace = device_plugin_service.config_namespace.clone();
mock.expect_create_instance()
@@ -1201,8 +1012,8 @@ mod device_plugin_service_tests {
async fn test_try_create_instance_already_created() {
let _ = env_logger::builder().is_test(true).try_init();
let (device_plugin_service, _device_plugin_service_receivers) =
- create_device_plugin_service(ConnectivityStatus::Online, false);
- let mut mock = MockKubeImpl::new();
+ create_device_plugin_service(InstanceConnectivityStatus::Online, false);
+ let mut mock = MockKubeInterface::new();
configure_find_configuration(
&mut mock,
device_plugin_service.config_name.clone(),
@@ -1213,7 +1024,7 @@ mod device_plugin_service_tests {
"../test/json/local-instance.json",
device_plugin_service.instance_name.clone(),
device_plugin_service.config_namespace.clone(),
- "",
+ String::new(),
NodeName::OtherNode,
);
let instance_name = device_plugin_service.instance_name.clone();
@@ -1244,8 +1055,8 @@ mod device_plugin_service_tests {
async fn test_try_create_instance_already_created_no_update() {
let _ = env_logger::builder().is_test(true).try_init();
let (device_plugin_service, _device_plugin_service_receivers) =
- create_device_plugin_service(ConnectivityStatus::Online, false);
- let mut mock = MockKubeImpl::new();
+ create_device_plugin_service(InstanceConnectivityStatus::Online, false);
+ let mut mock = MockKubeInterface::new();
configure_find_configuration(
&mut mock,
device_plugin_service.config_name.clone(),
@@ -1256,7 +1067,7 @@ mod device_plugin_service_tests {
"../test/json/local-instance.json",
device_plugin_service.instance_name.clone(),
device_plugin_service.config_namespace.clone(),
- "",
+ String::new(),
NodeName::ThisNode,
);
let dps = Arc::new(device_plugin_service);
@@ -1275,10 +1086,10 @@ mod device_plugin_service_tests {
async fn test_try_create_instance_no_config() {
let _ = env_logger::builder().is_test(true).try_init();
let (device_plugin_service, _device_plugin_service_receivers) =
- create_device_plugin_service(ConnectivityStatus::Online, false);
+ create_device_plugin_service(InstanceConnectivityStatus::Online, false);
let config_name = device_plugin_service.config_name.clone();
let config_namespace = device_plugin_service.config_namespace.clone();
- let mut mock = MockKubeImpl::new();
+ let mut mock = MockKubeInterface::new();
mock.expect_find_configuration()
.times(1)
.withf(move |name: &str, namespace: &str| {
@@ -1300,8 +1111,8 @@ mod device_plugin_service_tests {
async fn test_try_create_instance_error() {
let _ = env_logger::builder().is_test(true).try_init();
let (device_plugin_service, _device_plugin_service_receivers) =
- create_device_plugin_service(ConnectivityStatus::Online, false);
- let mut mock = MockKubeImpl::new();
+ create_device_plugin_service(InstanceConnectivityStatus::Online, false);
+ let mut mock = MockKubeInterface::new();
configure_find_configuration(
&mut mock,
device_plugin_service.config_name.clone(),
@@ -1316,7 +1127,7 @@ mod device_plugin_service_tests {
.withf(move |name: &str, namespace: &str| {
namespace == config_namespace && name == instance_name
})
- .returning(move |_, _| Err(None.ok_or("failure")?));
+ .returning(move |_, _| Err(get_kube_not_found_error()));
let instance_name = device_plugin_service.instance_name.clone();
let config_namespace = device_plugin_service.config_namespace.clone();
mock.expect_create_instance()
@@ -1346,7 +1157,7 @@ mod device_plugin_service_tests {
async fn test_list_and_watch() {
let _ = env_logger::builder().is_test(true).try_init();
let (device_plugin_service, device_plugin_service_receivers) =
- create_device_plugin_service(ConnectivityStatus::Online, false);
+ create_device_plugin_service(InstanceConnectivityStatus::Online, false);
let device_plugin_temp_dir = Builder::new().prefix("device-plugins-").tempdir().unwrap();
let socket_path: String = device_plugin_temp_dir
.path()
@@ -1357,14 +1168,16 @@ mod device_plugin_service_tests {
let list_and_watch_message_sender =
device_plugin_service.list_and_watch_message_sender.clone();
let instance_name = device_plugin_service.instance_name.clone();
- serve(
- device_plugin_service,
- socket_path.clone(),
- device_plugin_service_receivers.server_ender_receiver,
- )
- .await
- .unwrap();
- let channel = Endpoint::try_from("lttp://[::]:50051")
+ let device_plugin_builder = DevicePluginBuilder {};
+ device_plugin_builder
+ .serve(
+ device_plugin_service,
+ socket_path.clone(),
+ device_plugin_service_receivers.server_ender_receiver,
+ )
+ .await
+ .unwrap();
+ let channel = Endpoint::try_from("dummy://[::]:50051")
.unwrap()
.connect_with_connector(service_fn(move |_: Uri| {
UnixStream::connect(socket_path.clone())
@@ -1445,13 +1258,13 @@ mod device_plugin_service_tests {
assert!(result.is_err());
}
- // Tests when ConnectivityStatus is offline and unhealthy devices are returned
+ // Tests when InstanceConnectivityStatus is offline and unhealthy devices are returned
#[tokio::test]
async fn test_build_list_and_watch_response_offline() {
let _ = env_logger::builder().is_test(true).try_init();
let (device_plugin_service, _device_plugin_service_receivers) =
- create_device_plugin_service(ConnectivityStatus::Offline(Instant::now()), true);
- let mock = MockKubeImpl::new();
+ create_device_plugin_service(InstanceConnectivityStatus::Offline(Instant::now()), true);
+ let mock = MockKubeInterface::new();
let devices =
build_list_and_watch_response(Arc::new(device_plugin_service), Arc::new(mock))
.await
@@ -1466,19 +1279,16 @@ mod device_plugin_service_tests {
async fn test_build_list_and_watch_response_no_instance() {
let _ = env_logger::builder().is_test(true).try_init();
let (device_plugin_service, _device_plugin_service_receivers) =
- create_device_plugin_service(ConnectivityStatus::Online, true);
+ create_device_plugin_service(InstanceConnectivityStatus::Online, true);
let instance_name = device_plugin_service.instance_name.clone();
let instance_namespace = device_plugin_service.config_namespace.clone();
- let mut mock = MockKubeImpl::new();
+ let mut mock = MockKubeInterface::new();
mock.expect_find_instance()
.times(1)
.withf(move |name: &str, namespace: &str| {
namespace == instance_namespace && name == instance_name
})
- .returning(move |_, _| {
- let error = Error::new(ErrorKind::InvalidInput, "Instance doesn't exist");
- Err(Box::new(error))
- });
+ .returning(move |_, _| Err(get_kube_not_found_error()));
let devices =
build_list_and_watch_response(Arc::new(device_plugin_service), Arc::new(mock))
.await
@@ -1493,16 +1303,16 @@ mod device_plugin_service_tests {
async fn test_build_list_and_watch_response_no_instance_update() {
let _ = env_logger::builder().is_test(true).try_init();
let (device_plugin_service, _device_plugin_service_receivers) =
- create_device_plugin_service(ConnectivityStatus::Online, true);
+ create_device_plugin_service(InstanceConnectivityStatus::Online, true);
let instance_name = device_plugin_service.instance_name.clone();
let instance_namespace = device_plugin_service.config_namespace.clone();
- let mut mock = MockKubeImpl::new();
+ let mut mock = MockKubeInterface::new();
configure_find_instance(
&mut mock,
"../test/json/local-instance.json",
instance_name.clone(),
instance_namespace.clone(),
- "",
+ String::new(),
NodeName::ThisNode,
);
let devices =
@@ -1512,23 +1322,20 @@ mod device_plugin_service_tests {
check_devices(instance_name, devices);
}
- // Test when device_usage[id] == ""
- // internal_allocate should set device_usage[id] = m.nodeName, return
- #[tokio::test]
- async fn test_internal_allocate_success() {
- let _ = env_logger::builder().is_test(true).try_init();
- let (device_plugin_service, mut device_plugin_service_receivers) =
- create_device_plugin_service(ConnectivityStatus::Online, true);
+ fn setup_internal_allocate_tests(
+ mock: &mut MockKubeInterface,
+ device_plugin_service: &DevicePluginService,
+ formerly_allocated_node: String,
+ newly_allocated_node: String,
+ ) -> Request {
let device_usage_id_slot = format!("{}-0", device_plugin_service.instance_name);
let device_usage_id_slot_2 = device_usage_id_slot.clone();
- let node_name = device_plugin_service.node_name.clone();
- let mut mock = MockKubeImpl::new();
configure_find_instance(
- &mut mock,
+ mock,
"../test/json/local-instance.json",
device_plugin_service.instance_name.clone(),
device_plugin_service.config_namespace.clone(),
- "",
+ formerly_allocated_node,
NodeName::ThisNode,
);
mock.expect_update_instance()
@@ -1538,14 +1345,64 @@ mod device_plugin_service_tests {
.device_usage
.get(&device_usage_id_slot)
.unwrap()
- == &node_name
+ == &newly_allocated_node
})
.returning(move |_, _, _| Ok(()));
let devices_i_ds = vec![device_usage_id_slot_2];
let container_requests = vec![v1beta1::ContainerAllocateRequest { devices_i_ds }];
- let requests = Request::new(AllocateRequest { container_requests });
+ Request::new(AllocateRequest { container_requests })
+ }
+
+ // Test that environment variables set in a Configuration will be set in brokers
+ #[tokio::test]
+ async fn test_internal_allocate_env_vars() {
+ let _ = env_logger::builder().is_test(true).try_init();
+ let (device_plugin_service, mut device_plugin_service_receivers) =
+ create_device_plugin_service(InstanceConnectivityStatus::Online, true);
+ let node_name = device_plugin_service.node_name.clone();
+ let mut mock = MockKubeInterface::new();
+ let request = setup_internal_allocate_tests(
+ &mut mock,
+ &device_plugin_service,
+ String::new(),
+ node_name,
+ );
+ let broker_envs = device_plugin_service
+ .internal_allocate(request, Arc::new(mock))
+ .await
+ .unwrap()
+ .into_inner()
+ .container_responses[0]
+ .envs
+ .clone();
+ assert_eq!(broker_envs.get("RESOLUTION_WIDTH").unwrap(), "800");
+ assert_eq!(broker_envs.get("RESOLUTION_HEIGHT").unwrap(), "600");
+ // Check that Device properties are set as env vars by checking for
+ // property of device created in `create_device_plugin_service`
+ assert_eq!(broker_envs.get("DEVICE_LOCATION_INFO").unwrap(), "endpoint");
+ assert!(device_plugin_service_receivers
+ .list_and_watch_message_receiver
+ .try_recv()
+ .is_err());
+ }
+
+ // Test when device_usage[id] == ""
+ // internal_allocate should set device_usage[id] = m.nodeName, return
+ #[tokio::test]
+ async fn test_internal_allocate_success() {
+ let _ = env_logger::builder().is_test(true).try_init();
+ let (device_plugin_service, mut device_plugin_service_receivers) =
+ create_device_plugin_service(InstanceConnectivityStatus::Online, true);
+ let node_name = device_plugin_service.node_name.clone();
+ let mut mock = MockKubeInterface::new();
+ let request = setup_internal_allocate_tests(
+ &mut mock,
+ &device_plugin_service,
+ String::new(),
+ node_name,
+ );
assert!(device_plugin_service
- .internal_allocate(requests, Arc::new(mock),)
+ .internal_allocate(request, Arc::new(mock),)
.await
.is_ok());
assert!(device_plugin_service_receivers
@@ -1560,33 +1417,16 @@ mod device_plugin_service_tests {
async fn test_internal_allocate_deallocate() {
let _ = env_logger::builder().is_test(true).try_init();
let (device_plugin_service, mut device_plugin_service_receivers) =
- create_device_plugin_service(ConnectivityStatus::Online, true);
- let device_usage_id_slot = format!("{}-0", device_plugin_service.instance_name);
- let device_usage_id_slot_2 = device_usage_id_slot.clone();
- let mut mock = MockKubeImpl::new();
- configure_find_instance(
+ create_device_plugin_service(InstanceConnectivityStatus::Online, true);
+ let mut mock = MockKubeInterface::new();
+ let request = setup_internal_allocate_tests(
&mut mock,
- "../test/json/local-instance.json",
- device_plugin_service.instance_name.clone(),
- device_plugin_service.config_namespace.clone(),
- "node-a",
- NodeName::ThisNode,
+ &device_plugin_service,
+ "node-a".to_string(),
+ String::new(),
);
- mock.expect_update_instance()
- .times(1)
- .withf(move |instance_to_update: &Instance, _, _| {
- instance_to_update
- .device_usage
- .get(&device_usage_id_slot)
- .unwrap()
- == ""
- })
- .returning(move |_, _, _| Ok(()));
- let devices_i_ds = vec![device_usage_id_slot_2];
- let container_requests = vec![v1beta1::ContainerAllocateRequest { devices_i_ds }];
- let requests = Request::new(AllocateRequest { container_requests });
match device_plugin_service
- .internal_allocate(requests, Arc::new(mock))
+ .internal_allocate(request, Arc::new(mock))
.await
{
Ok(_) => {
@@ -1613,15 +1453,15 @@ mod device_plugin_service_tests {
async fn test_internal_allocate_taken() {
let _ = env_logger::builder().is_test(true).try_init();
let (device_plugin_service, mut device_plugin_service_receivers) =
- create_device_plugin_service(ConnectivityStatus::Online, true);
+ create_device_plugin_service(InstanceConnectivityStatus::Online, true);
let device_usage_id_slot = format!("{}-0", device_plugin_service.instance_name);
- let mut mock = MockKubeImpl::new();
+ let mut mock = MockKubeInterface::new();
configure_find_instance(
&mut mock,
"../test/json/local-instance.json",
device_plugin_service.instance_name.clone(),
device_plugin_service.config_namespace.clone(),
- "other",
+ "other".to_string(),
NodeName::ThisNode,
);
let devices_i_ds = vec![device_usage_id_slot];
@@ -1652,15 +1492,15 @@ mod device_plugin_service_tests {
async fn test_internal_allocate_no_id() {
let _ = env_logger::builder().is_test(true).try_init();
let (device_plugin_service, mut device_plugin_service_receivers) =
- create_device_plugin_service(ConnectivityStatus::Online, true);
+ create_device_plugin_service(InstanceConnectivityStatus::Online, true);
let device_usage_id_slot = format!("{}-100", device_plugin_service.instance_name);
- let mut mock = MockKubeImpl::new();
+ let mut mock = MockKubeInterface::new();
configure_find_instance(
&mut mock,
"../test/json/local-instance.json",
device_plugin_service.instance_name.clone(),
device_plugin_service.config_namespace.clone(),
- "other",
+ "other".to_string(),
NodeName::ThisNode,
);
let devices_i_ds = vec![device_usage_id_slot];
diff --git a/agent/src/util/discovery_operator.rs b/agent/src/util/discovery_operator.rs
new file mode 100644
index 000000000..d3241d9ae
--- /dev/null
+++ b/agent/src/util/discovery_operator.rs
@@ -0,0 +1,1783 @@
+use super::super::INSTANCE_COUNT_METRIC;
+use super::{
+ constants::{
+ DISCOVERY_HANDLER_OFFLINE_GRACE_PERIOD_SECS, SHARED_INSTANCE_OFFLINE_GRACE_PERIOD_SECS,
+ },
+ device_plugin_builder::{DevicePluginBuilder, DevicePluginBuilderInterface},
+ device_plugin_service,
+ device_plugin_service::{
+ get_device_instance_name, InstanceConnectivityStatus, InstanceInfo, InstanceMap,
+ },
+ embedded_discovery_handlers::get_discovery_handler,
+ registration::{
+ DiscoveryDetails, DiscoveryHandlerEndpoint, DiscoveryHandlerStatus,
+ RegisteredDiscoveryHandlerMap,
+ },
+ streaming_extension::StreamingExt,
+};
+use akri_discovery_utils::discovery::v0::{
+ discovery_handler_client::DiscoveryHandlerClient, Device, DiscoverRequest, DiscoverResponse,
+};
+use akri_shared::{
+ akri::configuration::KubeAkriConfig,
+ k8s,
+ os::env_var::{ActualEnvVarQuery, EnvVarQuery},
+};
+use blake2::{
+ digest::{Update, VariableOutput},
+ VarBlake2b,
+};
+use log::{error, trace};
+#[cfg(test)]
+use mock_instant::Instant;
+#[cfg(test)]
+use mockall::{automock, predicate::*};
+#[cfg(not(test))]
+use std::time::Instant;
+use std::{collections::HashMap, convert::TryFrom, sync::Arc};
+use tokio::sync::mpsc;
+use tonic::{
+ transport::{Endpoint, Uri},
+ Status,
+};
+
+/// StreamType provides a wrapper around the two different types of streams returned from embedded
+/// or embedded discovery handlers and ones running externally.
+pub enum StreamType {
+ Embedded(mpsc::Receiver>),
+ External(tonic::Streaming),
+}
+
+/// A DiscoveryOperator is created for each Configuration that is applied to the cluster.
+/// It handles discovery of the devices specified in a Configuration by calling `Discover` on
+/// all `DiscoveryHandlers` registered with name `Configuration.discovery_handler.name.`
+/// For each device discovered by the discovery handlers, it creates a device plugin.
+/// If a device disappears, it deletes the associated instance after a grace period (for non-local devices).
+/// Note: Since this structure is automocked, the compiler does not seem to be able to confirm that all the
+/// methods are being used. Therefore, #[allow(dead_code)] has been added to all methods that are not invoked or
+/// tested on a DiscoveryOperator.
+#[derive(Clone)]
+pub struct DiscoveryOperator {
+ /// Map of registered discovery handlers
+ discovery_handler_map: RegisteredDiscoveryHandlerMap,
+ /// The Akri Configuration associated with this `DiscoveryOperator`.
+ /// The Configuration tells the `DiscoveryOperator` what to look for.
+ config: KubeAkriConfig,
+ /// Map of Akri Instances discovered by this `DiscoveryOperator`
+ instance_map: InstanceMap,
+}
+
+#[cfg_attr(test, automock)]
+impl DiscoveryOperator {
+ pub fn new(
+ discovery_handler_map: RegisteredDiscoveryHandlerMap,
+ config: KubeAkriConfig,
+ instance_map: InstanceMap,
+ ) -> Self {
+ DiscoveryOperator {
+ discovery_handler_map,
+ config,
+ instance_map,
+ }
+ }
+ /// Returns discovery_handler_map field. Allows the struct to be mocked.
+ #[allow(dead_code)]
+ pub fn get_discovery_handler_map(&self) -> RegisteredDiscoveryHandlerMap {
+ self.discovery_handler_map.clone()
+ }
+ /// Returns config field. Allows the struct to be mocked.
+ #[allow(dead_code)]
+ pub fn get_config(&self) -> KubeAkriConfig {
+ self.config.clone()
+ }
+ /// Returns instance_map field. Allows the struct to be mocked.
+ #[allow(dead_code)]
+ pub fn get_instance_map(&self) -> InstanceMap {
+ self.instance_map.clone()
+ }
+ #[allow(dead_code)]
+ pub async fn stop_all_discovery(&self) {
+ let mut discovery_handler_map = self.discovery_handler_map.lock().unwrap().clone();
+ if let Some(discovery_handler_details_map) =
+ discovery_handler_map.get_mut(&self.config.spec.discovery_handler.name)
+ {
+ for (endpoint, dh_details) in discovery_handler_details_map.clone() {
+ match dh_details.close_discovery_handler_connection.send(()) {
+ Ok(_) => trace!("stop_all_discovery - discovery client for {} discovery handler at endpoint {:?} told to stop", self.config.spec.discovery_handler.name, endpoint),
+ Err(e) => error!("stop_all_discovery - discovery client {} discovery handler at endpoint {:?} could not receive stop message with error {:?}", self.config.spec.discovery_handler.name, endpoint, e)
+ }
+ }
+ }
+ }
+
+ /// Calls discover on the Discovery Handler at the given endpoint and returns the connection stream.
+ pub async fn get_stream(&self, endpoint: &DiscoveryHandlerEndpoint) -> Option {
+ let discover_request = tonic::Request::new(DiscoverRequest {
+ discovery_details: self.config.spec.discovery_handler.discovery_details.clone(),
+ });
+ trace!("get_stream - endpoint is {:?}", endpoint);
+ match endpoint {
+ DiscoveryHandlerEndpoint::Embedded => {
+ match get_discovery_handler(&self.config.spec.discovery_handler) {
+ Ok(discovery_handler) => {
+ trace!(
+ "get_stream - using embedded {} discovery handler",
+ self.config.spec.discovery_handler.name
+ );
+ match discovery_handler.discover(discover_request).await {
+ Ok(device_update_receiver) => {
+ Some(StreamType::Embedded(device_update_receiver.into_inner()))
+ }
+ Err(e) => {
+ error!("get_stream - could not connect to DiscoveryHandler at endpoint {:?} with error {}", endpoint, e);
+ None
+ }
+ }
+ }
+ Err(e) => {
+ error!("get_stream - no embedded discovery handler found with name {} with error {:?}", self.config.spec.discovery_handler.name, e);
+ None
+ }
+ }
+ }
+ DiscoveryHandlerEndpoint::Uds(socket) => {
+ // Clone socket for closure which has static lifetime
+ let socket = socket.clone();
+ // We will ignore this dummy uri because UDS does not use it.
+ match Endpoint::try_from("dummy://[::]:50051")
+ .unwrap()
+ .connect_with_connector(tower::service_fn(move |_: Uri| {
+ let endpoint = socket.clone();
+ tokio::net::UnixStream::connect(endpoint)
+ }))
+ .await
+ {
+ Ok(channel) => {
+ trace!(
+ "get_stream - connecting to external {} discovery handler over UDS",
+ self.config.spec.discovery_handler.name
+ );
+ let mut discovery_handler_client = DiscoveryHandlerClient::new(channel);
+ match discovery_handler_client.discover(discover_request).await {
+ Ok(device_update_receiver) => {
+ Some(StreamType::External(device_update_receiver.into_inner()))
+ }
+ Err(e) => {
+ error!("get_stream - could not connect to DiscoveryHandler at endpoint {:?} with error {}", endpoint, e);
+ None
+ }
+ }
+ }
+ Err(e) => {
+ error!("get_stream - failed to connect to {} discovery handler over UDS with error {}", self.config.spec.discovery_handler.name, e);
+ None
+ }
+ }
+ }
+ DiscoveryHandlerEndpoint::Network(addr) => {
+ match DiscoveryHandlerClient::connect(addr.clone()).await {
+ Ok(mut discovery_handler_client) => {
+ trace!(
+ "get_stream - connecting to external {} discovery handler over network",
+ self.config.spec.discovery_handler.name
+ );
+ match discovery_handler_client.discover(discover_request).await {
+ Ok(device_update_receiver) => {
+ Some(StreamType::External(device_update_receiver.into_inner()))
+ }
+ Err(e) => {
+ error!("get_stream - could not connect to DiscoveryHandler at endpoint {:?} with error {}", endpoint, e);
+ None
+ }
+ }
+ }
+ Err(e) => {
+ error!("get_stream - failed to connect to {} discovery handler over network with error {}", self.config.spec.discovery_handler.name, e);
+ None
+ }
+ }
+ }
+ }
+ }
+ /// Listens for new discovery responses and calls a function to handle the new discovery results.
+ /// Runs until notified to stop discovery.
+ #[allow(dead_code)]
+ pub async fn internal_do_discover<'a>(
+ &'a self,
+ kube_interface: Arc>,
+ dh_details: &'a DiscoveryDetails,
+ stream: &'a mut dyn StreamingExt,
+ ) -> Result<(), Status> {
+ // clone objects for thread
+ let discovery_operator = Arc::new(self.clone());
+ let stop_discovery_receiver: &mut tokio::sync::broadcast::Receiver<()> =
+ &mut dh_details.close_discovery_handler_connection.subscribe();
+ loop {
+ // Wait for either new discovery results or a message to stop discovery
+ tokio::select! {
+ _ = stop_discovery_receiver.recv() => {
+ trace!("internal_do_discover - received message to stop discovery for endpoint {:?} serving protocol {}", dh_details.endpoint, discovery_operator.get_config().spec.discovery_handler.name);
+ break;
+ },
+ result = stream.get_message() => {
+ let message = result?;
+ if let Some(response) = message {
+ trace!("internal_do_discover - got discovery results {:?}", response.devices);
+ self.handle_discovery_results(
+ kube_interface.clone(),
+ response.devices,
+ dh_details.shared,
+ Box::new(DevicePluginBuilder{}),
+ )
+ .await
+ .unwrap();
+ } else {
+ error!("internal_do_discover - received result of type None. Should not happen.");
+ break;
+ }
+ }
+ }
+ }
+
+ Ok(())
+ }
+
+ /// Sets the connectivity status of a discovery handler. If a discovery handler goes offline, mark_offline_or_deregister_discovery_handler should be used.
+ pub fn set_discovery_handler_connectivity_status(
+ &self,
+ endpoint: &DiscoveryHandlerEndpoint,
+ connectivity_status: DiscoveryHandlerStatus,
+ ) {
+ trace!("set_discovery_handler_connectivity_status - set status of {:?} for {} discovery handler at endpoint {:?}", connectivity_status, self.config.spec.discovery_handler.name, endpoint);
+ let mut registered_dh_map = self.discovery_handler_map.lock().unwrap();
+ let discovery_handler_details_map = registered_dh_map
+ .get_mut(&self.config.spec.discovery_handler.name)
+ .unwrap();
+ let dh_details = discovery_handler_details_map.get_mut(endpoint).unwrap();
+ dh_details.connectivity_status = connectivity_status;
+ }
+
+ /// This is called when no connection can be made with a discovery handler at its endpoint.
+ /// It takes action based on a Discovery Handler's (DH's) current `DiscoveryHandlerStatus`.
+ /// If `DiscoveryHandlerStatus::Waiting`, connectivity status changed to Offline.
+ /// If `DiscoveryHandlerStatus::Offline`, DH is removed from the `RegisteredDiscoveryHandlersMap`
+ /// if it have been offline for longer than the grace period.
+ /// If `DiscoveryHandlerStatus::Active`, this should not happen, Error is returned.
+ pub async fn mark_offline_or_deregister_discovery_handler(
+ &self,
+ endpoint: &DiscoveryHandlerEndpoint,
+ ) -> Result {
+ trace!("mark_offline_or_deregister_discovery_handler - {} discovery handler at endpoint {:?} is offline", self.config.spec.discovery_handler.name, endpoint);
+ let mut deregistered = false;
+ let mut registered_dh_map = self.discovery_handler_map.lock().unwrap();
+ let discovery_handler_details_map = registered_dh_map
+ .get_mut(&self.config.spec.discovery_handler.name)
+ .unwrap();
+ let dh_details = discovery_handler_details_map.get_mut(endpoint).unwrap();
+ match dh_details.connectivity_status {
+ DiscoveryHandlerStatus::Offline(instant) => {
+ if instant.elapsed().as_secs() > DISCOVERY_HANDLER_OFFLINE_GRACE_PERIOD_SECS {
+ trace!("mark_offline_or_deregister_discovery_handler - de-registering {} discovery handler at endpoint {:?} since been offline for longer than 5 minutes", self.config.spec.discovery_handler.name, endpoint);
+ // Remove discovery handler from map if timed out
+ discovery_handler_details_map.remove(endpoint).unwrap();
+ deregistered = true;
+ }
+ }
+ DiscoveryHandlerStatus::Waiting | DiscoveryHandlerStatus::Active => {
+ dh_details.connectivity_status = DiscoveryHandlerStatus::Offline(Instant::now());
+ }
+ }
+ Ok(deregistered)
+ }
+
+ /// Checks if any of this DiscoveryOperator's Configuration's Instances have been offline for too long.
+ /// If a non-local device has not come back online before `SHARED_INSTANCE_OFFLINE_GRACE_PERIOD_SECS`,
+ /// the associated Device Plugin and Instance are terminated and deleted, respectively.
+ pub async fn delete_offline_instances(
+ &self,
+ kube_interface: Arc>,
+ ) -> Result<(), Box> {
+ trace!(
+ "delete_offline_instances - entered for configuration {}",
+ self.config.metadata.name
+ );
+ let kube_interface_clone = kube_interface.clone();
+ let instance_map = self.instance_map.lock().await.clone();
+ for (instance, instance_info) in instance_map.clone() {
+ if let InstanceConnectivityStatus::Offline(instant) = instance_info.connectivity_status
+ {
+ let time_offline = instant.elapsed().as_secs();
+ // If instance has been offline for longer than the grace period or it is unshared, terminate the associated device plugin
+ // TODO: make grace period configurable
+ if time_offline >= SHARED_INSTANCE_OFFLINE_GRACE_PERIOD_SECS {
+ trace!("delete_offline_instances - instance {} has been offline too long ... terminating device plugin", instance);
+ device_plugin_service::terminate_device_plugin_service(
+ &instance,
+ self.instance_map.clone(),
+ )
+ .await
+ .unwrap();
+ k8s::try_delete_instance(
+ (*kube_interface_clone).as_ref(),
+ &instance,
+ self.config.metadata.namespace.as_ref().unwrap(),
+ )
+ .await?;
+ }
+ }
+ }
+ Ok(())
+ }
+
+ /// Takes in a list of discovered devices and determines if there are any new devices or no longer visible devices.
+ /// For each new device, it creates a DevicePluginService.
+ /// For each previously visible device that was no longer discovered, it calls a function that updates the InstanceConnectivityStatus
+ /// of the instance or deletes it if it is a local device.
+ pub async fn handle_discovery_results(
+ &self,
+ kube_interface: Arc>,
+ discovery_results: Vec,
+ shared: bool,
+ device_plugin_builder: Box,
+ ) -> Result<(), Box> {
+ trace!(
+ "handle_discovery_results - for config {} with discovery results {:?}",
+ self.config.metadata.name,
+ discovery_results
+ );
+ let currently_visible_instances: HashMap = discovery_results
+ .iter()
+ .map(|discovery_result| {
+ let id = generate_instance_digest(&discovery_result.id, shared);
+ let instance_name = get_device_instance_name(&id, &self.config.metadata.name);
+ (instance_name, discovery_result.clone())
+ })
+ .collect();
+ INSTANCE_COUNT_METRIC
+ .with_label_values(&[&self.config.metadata.name, &shared.to_string()])
+ .set(currently_visible_instances.len() as i64);
+ // Update the connectivity status of instances and return list of visible instances that don't have Instance CRs
+ let instance_map = self.instance_map.lock().await.clone();
+ // Find all visible instances that do not have Instance CRDs yet
+ let new_discovery_results: Vec = currently_visible_instances
+ .iter()
+ .filter(|(name, _)| !instance_map.contains_key(*name))
+ .map(|(_, p)| p.clone())
+ .collect();
+ self.update_instance_connectivity_status(
+ kube_interface,
+ currently_visible_instances,
+ shared,
+ )
+ .await?;
+
+ // If there are newly visible instances associated with a Config, make a device plugin and Instance CR for them
+ if !new_discovery_results.is_empty() {
+ for discovery_result in new_discovery_results {
+ let id = generate_instance_digest(&discovery_result.id, shared);
+ let instance_name = get_device_instance_name(&id, &self.config.metadata.name);
+ trace!(
+ "handle_discovery_results - new instance {} came online",
+ instance_name
+ );
+ let instance_map = self.instance_map.clone();
+ if let Err(e) = device_plugin_builder
+ .build_device_plugin(
+ instance_name,
+ &self.config,
+ shared,
+ instance_map,
+ discovery_result.clone(),
+ )
+ .await
+ {
+ error!("handle_discovery_results - error {} building device plugin ... trying again on next iteration", e);
+ }
+ }
+ }
+ Ok(())
+ }
+
+ /// Takes in a list of currently visible instances and either updates an Instance's InstanceConnectivityStatus or deletes an Instance.
+ /// If a non-local/network based device is not longer visible it's InstanceConnectivityStatus is changed to Offline(time now).
+ /// The associated DevicePluginService checks its InstanceConnectivityStatus before sending a response back to kubelet
+ /// and will send all unhealthy devices if its status is Offline, preventing kubelet from allocating any more pods to it.
+ /// An Instance CRD is deleted and it's DevicePluginService shutdown if its:
+ /// (A) non-local Instance is still not visible after 5 minutes or (B) local instance is still not visible.
+ pub async fn update_instance_connectivity_status(
+ &self,
+ kube_interface: Arc>,
+ currently_visible_instances: HashMap,
+ shared: bool,
+ ) -> Result<(), Box> {
+ let instance_map = self.instance_map.lock().await.clone();
+ for (instance, instance_info) in instance_map {
+ trace!(
+ "update_instance_connectivity_status - checking connectivity status of instance {}",
+ instance
+ );
+ if currently_visible_instances.contains_key(&instance) {
+ let connectivity_status = instance_info.connectivity_status;
+ // If instance is visible, make sure connectivity status is (updated to be) Online
+ if let InstanceConnectivityStatus::Offline(_instant) = connectivity_status {
+ trace!(
+ "update_instance_connectivity_status - instance {} that was temporarily offline is back online",
+ instance
+ );
+ let list_and_watch_message_sender = instance_info.list_and_watch_message_sender;
+ let updated_instance_info = InstanceInfo {
+ connectivity_status: InstanceConnectivityStatus::Online,
+ list_and_watch_message_sender: list_and_watch_message_sender.clone(),
+ };
+ self.instance_map
+ .lock()
+ .await
+ .insert(instance.clone(), updated_instance_info);
+ // Signal list_and_watch to update kubelet that the devices are healthy.
+ list_and_watch_message_sender
+ .send(device_plugin_service::ListAndWatchMessageKind::Continue)
+ .unwrap();
+ } else {
+ trace!(
+ "update_instance_connectivity_status - instance {} still online",
+ instance
+ );
+ }
+ } else {
+ // If the instance is not visible:
+ // // If the instance is local, remove it
+ // // If the instance is not local
+ // // // If it has not already been labeled offline, label it
+ // // // If the instance has already been labeled offline
+ // // // remove instance from map if grace period has elapsed without the instance coming back online
+ let mut remove_instance = false;
+ match instance_info.connectivity_status {
+ InstanceConnectivityStatus::Online => {
+ if !shared {
+ remove_instance = true;
+ } else {
+ let sender = instance_info.list_and_watch_message_sender.clone();
+ let updated_instance_info = InstanceInfo {
+ connectivity_status: InstanceConnectivityStatus::Offline(
+ Instant::now(),
+ ),
+ list_and_watch_message_sender: instance_info
+ .list_and_watch_message_sender
+ .clone(),
+ };
+ self.instance_map
+ .lock()
+ .await
+ .insert(instance.clone(), updated_instance_info);
+ trace!(
+ "update_instance_connectivity_status - instance {} went offline ... starting timer and forcing list_and_watch to continue",
+ instance
+ );
+ sender
+ .send(device_plugin_service::ListAndWatchMessageKind::Continue)
+ .unwrap();
+ }
+ }
+ InstanceConnectivityStatus::Offline(instant) => {
+ let time_offline = instant.elapsed().as_secs();
+ // If instance has been offline for longer than the grace period, terminate the associated device plugin
+ if time_offline >= SHARED_INSTANCE_OFFLINE_GRACE_PERIOD_SECS {
+ remove_instance = true;
+ }
+ }
+ }
+ if remove_instance {
+ trace!("update_instance_connectivity_status - instance {} has been offline too long ... terminating device plugin", instance);
+ device_plugin_service::terminate_device_plugin_service(
+ &instance,
+ self.instance_map.clone(),
+ )
+ .await
+ .unwrap();
+ k8s::try_delete_instance(
+ (*kube_interface).as_ref(),
+ &instance,
+ self.config.metadata.namespace.as_ref().unwrap(),
+ )
+ .await
+ .unwrap();
+ }
+ }
+ }
+ Ok(())
+ }
+}
+
+pub mod start_discovery {
+ use super::super::registration::{
+ DiscoveryDetails, DiscoveryHandlerEndpoint, DiscoveryHandlerStatus,
+ };
+ // Use this `mockall` macro to automate importing a mock type in test mode, or a real type otherwise.
+ #[double]
+ pub use super::DiscoveryOperator;
+ use super::StreamType;
+ use akri_shared::k8s;
+ use mockall_double::double;
+ use std::{sync::Arc, time::Duration};
+ use tokio::sync::{broadcast, mpsc};
+
+ /// This is spawned as a task for each Configuration and continues to run
+ /// until the Configuration is deleted, at which point, this function is signaled to stop.
+ /// It consists of three subtasks:
+ /// 1) Initiates discovery on all already registered discovery handlers in the RegisteredDiscoveryHandlerMap
+ /// with the same discovery handler name as the Configuration (Configuration.discovery_handler.name).
+ /// 2) Listens for new discover handlers to come online for this Configuration and initiates discovery.
+ /// 3) Checks whether Offline Instances have exceeded their grace period, in which case it
+ /// deletes the Instance.
+ pub async fn start_discovery(
+ discovery_operator: DiscoveryOperator,
+ new_discovery_handler_sender: broadcast::Sender,
+ stop_all_discovery_sender: broadcast::Sender<()>,
+ finished_all_discovery_sender: &mut mpsc::Sender<()>,
+ kube_interface: Arc>,
+ ) -> Result<(), Box> {
+ let config = discovery_operator.get_config();
+ info!(
+ "start_discovery - entered for {} discovery handler",
+ config.spec.discovery_handler.name
+ );
+ let config_name = config.metadata.name.clone();
+ let mut tasks = Vec::new();
+ let discovery_operator = Arc::new(discovery_operator);
+
+ // Call discover on already registered Discovery Handlers requested by this Configuration's
+ let known_dh_discovery_operator = discovery_operator.clone();
+ let known_dh_kube_interface = kube_interface.clone();
+ tasks.push(tokio::spawn(async move {
+ do_discover(known_dh_discovery_operator, known_dh_kube_interface)
+ .await
+ .unwrap();
+ }));
+
+ // Listen for new discovery handlers to call discover on
+ let mut stop_all_discovery_receiver = stop_all_discovery_sender.subscribe();
+ let mut new_discovery_handler_receiver = new_discovery_handler_sender.subscribe();
+ let new_dh_discovery_operator = discovery_operator.clone();
+ tasks.push(tokio::spawn(async move {
+ listen_for_new_discovery_handlers(
+ new_dh_discovery_operator,
+ &mut new_discovery_handler_receiver,
+ &mut stop_all_discovery_receiver,
+ )
+ .await
+ .unwrap();
+ }));
+
+ // Non-local devices are only allowed to be offline for `SHARED_INSTANCE_OFFLINE_GRACE_PERIOD_SECS` minutes before being removed.
+ // This task periodically checks if devices have been offline for too long.
+ let mut stop_all_discovery_receiver = stop_all_discovery_sender.subscribe();
+ let offline_dh_discovery_operator = discovery_operator.clone();
+ let offline_dh_kube_interface = kube_interface.clone();
+ tasks.push(tokio::spawn(async move {
+ loop {
+ offline_dh_discovery_operator
+ .delete_offline_instances(offline_dh_kube_interface.clone())
+ .await
+ .unwrap();
+ if tokio::time::timeout(
+ Duration::from_secs(30),
+ stop_all_discovery_receiver.recv(),
+ )
+ .await.is_ok()
+ {
+ trace!("start_discovery - received message to stop checking connectivity status for configuration {}", config_name);
+ break;
+ }
+ }
+ }));
+ futures::future::try_join_all(tasks).await?;
+ finished_all_discovery_sender.send(()).await?;
+ Ok(())
+ }
+
+ /// Waits to be notified of new discovery handlers. If the discovery handler does discovery for this Configuration,
+ /// discovery is kicked off.
+ async fn listen_for_new_discovery_handlers(
+ discovery_operator: Arc,
+ new_discovery_handler_receiver: &mut broadcast::Receiver,
+ stop_all_discovery_receiver: &mut broadcast::Receiver<()>,
+ ) -> Result<(), Box> {
+ let mut discovery_tasks = Vec::new();
+ loop {
+ tokio::select! {
+ _ = stop_all_discovery_receiver.recv() => {
+ trace!("listen_for_new_discovery_handlers - received message to stop discovery for configuration {}", discovery_operator.get_config().metadata.name);
+ discovery_operator.stop_all_discovery().await;
+ break;
+ },
+ result = new_discovery_handler_receiver.recv() => {
+ // Check if it is one of this Configuration's discovery handlers
+ if let Ok(discovery_handler_name) = result {
+ if discovery_handler_name == discovery_operator.get_config().spec.discovery_handler.name {
+ trace!("listen_for_new_discovery_handlers - received new registered discovery handler for configuration {}", discovery_operator.get_config().metadata.name);
+ let new_discovery_operator = discovery_operator.clone();
+ discovery_tasks.push(tokio::spawn(async move {
+ do_discover(new_discovery_operator, Arc::new(Box::new(k8s::create_kube_interface()))).await.unwrap();
+ }));
+ }
+ }
+ }
+ }
+ }
+ // Wait for all discovery handlers to complete discovery
+ futures::future::try_join_all(discovery_tasks).await?;
+ Ok(())
+ }
+
+ /// A Configuration specifies the name of `DiscoveryHandlers` that should be utilized for discovery.
+ /// This tries to establish connection with each `DiscoveryHandler` registered under the requested
+ /// `DiscoveryHandler` name and spawns a discovery thread for each connection.
+ /// This function also manages the `DiscoveryHandlerStatus` of each `DiscoveryHandler` as follows:
+ /// /// `DiscoveryHandlerStatus::Active` if a connection is established via a call to get_stream
+ /// /// `DiscoveryHandlerStatus::Waitin`g after a connection has finished due to either being signaled to stop connecting
+ /// /// or an error being returned from the discovery handler (that is not a broken pipe)
+ /// /// `DiscoveryHandlerStatus::Offline` if a connection cannot be established via a call to get_stream
+ /// If a connection cannot be established, continues to try, sleeping between iteration.
+ /// Removes the discovery handler from the `RegisteredDiscoveryHandlerMap` if it has been offline for longer than the grace period.
+ pub async fn do_discover(
+ discovery_operator: Arc,
+ kube_interface: Arc>,
+ ) -> Result<(), Box> {
+ let mut discovery_tasks = Vec::new();
+ let config = discovery_operator.get_config();
+ trace!(
+ "do_discover - entered for {} discovery handler",
+ config.spec.discovery_handler.name
+ );
+ // get clone of map
+ let mut discovery_handler_map = discovery_operator
+ .get_discovery_handler_map()
+ .lock()
+ .unwrap()
+ .clone();
+ trace!(
+ "do_discover - discovery_handler_map is {:?}",
+ discovery_handler_map
+ );
+ if let Some(discovery_handler_details_map) =
+ discovery_handler_map.get_mut(&config.spec.discovery_handler.name)
+ {
+ for (endpoint, dh_details) in discovery_handler_details_map.clone() {
+ trace!(
+ "do_discover - for {} discovery handler at endpoint {:?}",
+ config.spec.discovery_handler.name,
+ endpoint
+ );
+ // Only use DiscoveryHandler if it doesn't have a client yet
+ if dh_details.connectivity_status != DiscoveryHandlerStatus::Active {
+ trace!(
+ "do_discover - {} discovery handler at endpoint {:?} doesn't have client",
+ config.spec.discovery_handler.name,
+ endpoint
+ );
+ let discovery_operator = discovery_operator.clone();
+ let kube_interface = kube_interface.clone();
+ discovery_tasks.push(tokio::spawn(async move {
+ do_discover_on_discovery_handler(
+ discovery_operator.clone(),
+ kube_interface.clone(),
+ &endpoint,
+ &dh_details,
+ )
+ .await
+ .unwrap();
+ }));
+ }
+ }
+ }
+ futures::future::try_join_all(discovery_tasks).await?;
+ Ok(())
+ }
+
+ /// Try to connect to discovery handler until connection has been established or grace period has passed
+ async fn do_discover_on_discovery_handler<'a>(
+ discovery_operator: Arc,
+ kube_interface: Arc>,
+ endpoint: &'a DiscoveryHandlerEndpoint,
+ dh_details: &'a DiscoveryDetails,
+ ) -> Result<(), Box> {
+ loop {
+ let deregistered;
+ match discovery_operator.get_stream(&endpoint).await {
+ Some(stream_type) => {
+ // Since connection was established, be sure that the Discovery Handler is marked as having a client
+ discovery_operator.set_discovery_handler_connectivity_status(
+ &endpoint,
+ DiscoveryHandlerStatus::Active,
+ );
+ match stream_type {
+ StreamType::External(mut stream) => {
+ match discovery_operator
+ .internal_do_discover(
+ kube_interface.clone(),
+ &dh_details,
+ &mut stream,
+ )
+ .await
+ {
+ Ok(_) => {
+ discovery_operator.set_discovery_handler_connectivity_status(
+ &endpoint,
+ DiscoveryHandlerStatus::Waiting,
+ );
+ break;
+ }
+ Err(status) => {
+ if status.message().contains("broken pipe") {
+ // Mark all associated instances as offline
+ error!("do_discover_on_discovery_handler - connection with Discovery Handler dropped with status {:?}. Marking all instances offline.", status);
+ discovery_operator
+ .update_instance_connectivity_status(
+ kube_interface.clone(),
+ std::collections::HashMap::new(),
+ dh_details.shared,
+ )
+ .await?;
+ deregistered = discovery_operator
+ .mark_offline_or_deregister_discovery_handler(&endpoint)
+ .await
+ .unwrap();
+ } else {
+ trace!("do_discover_on_discovery_handler - Discovery Handlers returned error status {}. Marking all instances offline.", status);
+ // TODO: Possibly mark config as invalid
+ // Mark all associated instances as offline by declaring no visible instances
+ discovery_operator
+ .update_instance_connectivity_status(
+ kube_interface.clone(),
+ std::collections::HashMap::new(),
+ dh_details.shared,
+ )
+ .await?;
+ discovery_operator
+ .set_discovery_handler_connectivity_status(
+ &endpoint,
+ DiscoveryHandlerStatus::Waiting,
+ );
+ break;
+ }
+ }
+ }
+ }
+ StreamType::Embedded(mut stream) => {
+ discovery_operator
+ .internal_do_discover(
+ kube_interface.clone(),
+ &dh_details,
+ &mut stream,
+ )
+ .await
+ .unwrap();
+ discovery_operator.set_discovery_handler_connectivity_status(
+ &endpoint,
+ DiscoveryHandlerStatus::Waiting,
+ );
+ break;
+ }
+ }
+ }
+ None => {
+ deregistered = discovery_operator
+ .mark_offline_or_deregister_discovery_handler(&endpoint)
+ .await
+ .unwrap();
+ }
+ }
+ if deregistered {
+ break;
+ } else {
+ // If a connection cannot be established with the Discovery Handler, it will sleep and try again.
+ // This continues until connection established, the Discovery Handler is deregistered due to grace period elapsing,
+ // or the Discovery Handler is told to stop discovery.
+ let mut stop_discovery_receiver =
+ dh_details.close_discovery_handler_connection.subscribe();
+ let mut sleep_duration = Duration::from_secs(60);
+ if cfg!(test) {
+ sleep_duration = Duration::from_millis(100);
+ }
+
+ if tokio::time::timeout(sleep_duration, stop_discovery_receiver.recv())
+ .await
+ .is_ok()
+ {
+ trace!("do_discover_on_discovery_handler - received message to stop discovery for {} Discovery Handler at endpoint {:?}", dh_details.name, dh_details.endpoint);
+ break;
+ }
+ }
+ }
+ Ok(())
+ }
+}
+
+/// Generates an digest of an Instance's id. There should be a unique digest and Instance for each discovered device.
+/// This means that the id of non-local devices that could be visible to multiple nodes should always resolve
+/// to the same instance name (which is suffixed with this digest).
+/// However, local devices' Instances should have unique hashes even if they have the same id.
+/// To ensure this, the node's name is added to the id before it is hashed.
+pub fn generate_instance_digest(id_to_digest: &str, shared: bool) -> String {
+ let env_var_query = ActualEnvVarQuery {};
+ inner_generate_instance_digest(id_to_digest, shared, &env_var_query)
+}
+
+pub fn inner_generate_instance_digest(
+ id_to_digest: &str,
+ shared: bool,
+ query: &impl EnvVarQuery,
+) -> String {
+ let mut id_to_digest = id_to_digest.to_string();
+ // For local devices, include node hostname in id_to_digest so instances have unique names
+ if !shared {
+ id_to_digest = format!(
+ "{}{}",
+ &id_to_digest,
+ query.get_env_var("AGENT_NODE_NAME").unwrap()
+ );
+ }
+ let mut digest = String::new();
+ let mut hasher = VarBlake2b::new(3).unwrap();
+ hasher.update(id_to_digest);
+ hasher.finalize_variable(|var| {
+ digest = var
+ .iter()
+ .map(|num| format!("{:02x}", num))
+ .collect::>()
+ .join("")
+ });
+ digest
+}
+
+#[cfg(test)]
+pub mod tests {
+ use super::super::{
+ device_plugin_builder::MockDevicePluginBuilderInterface,
+ registration::{
+ inner_register_embedded_discovery_handlers, DiscoveryDetails, DiscoveryHandlerStatus,
+ },
+ };
+ use super::*;
+ use akri_discovery_utils::discovery::mock_discovery_handler;
+ use akri_shared::{
+ akri::configuration::KubeAkriConfig, k8s::MockKubeInterface, os::env_var::MockEnvVarQuery,
+ };
+ use mock_instant::{Instant, MockClock};
+ use mockall::Sequence;
+ use std::time::Duration;
+ use tokio::sync::broadcast;
+
+ pub async fn build_instance_map(
+ config: &KubeAkriConfig,
+ visible_discovery_results: &mut Vec,
+ list_and_watch_message_receivers: &mut Vec<
+ broadcast::Receiver,
+ >,
+ connectivity_status: InstanceConnectivityStatus,
+ ) -> InstanceMap {
+ let device1 = Device {
+ id: "filter1".to_string(),
+ properties: HashMap::new(),
+ mounts: Vec::default(),
+ device_specs: Vec::default(),
+ };
+ let device2 = Device {
+ id: "filter2".to_string(),
+ properties: HashMap::new(),
+ mounts: Vec::default(),
+ device_specs: Vec::default(),
+ };
+ let discovery_results = vec![device1, device2];
+ *visible_discovery_results = discovery_results.clone();
+ generate_instance_map(
+ discovery_results,
+ list_and_watch_message_receivers,
+ connectivity_status,
+ &config.metadata.name,
+ )
+ }
+
+ fn generate_instance_map(
+ discovery_results: Vec,
+ list_and_watch_message_receivers: &mut Vec<
+ broadcast::Receiver,
+ >,
+ connectivity_status: InstanceConnectivityStatus,
+ config_name: &str,
+ ) -> InstanceMap {
+ Arc::new(tokio::sync::Mutex::new(
+ discovery_results
+ .iter()
+ .map(|device| {
+ let (list_and_watch_message_sender, list_and_watch_message_receiver) =
+ broadcast::channel(2);
+ list_and_watch_message_receivers.push(list_and_watch_message_receiver);
+ let instance_name = get_device_instance_name(&device.id, &config_name);
+ (
+ instance_name,
+ InstanceInfo {
+ list_and_watch_message_sender,
+ connectivity_status: connectivity_status.clone(),
+ },
+ )
+ })
+ .collect(),
+ ))
+ }
+
+ fn create_mock_discovery_operator(
+ discovery_handler_map: RegisteredDiscoveryHandlerMap,
+ config: KubeAkriConfig,
+ instance_map: InstanceMap,
+ ) -> MockDiscoveryOperator {
+ let ctx = MockDiscoveryOperator::new_context();
+ let discovery_handler_map_clone = discovery_handler_map.clone();
+ let config_clone = config.clone();
+ let instance_map_clone = instance_map.clone();
+ ctx.expect().return_once(move |_, _, _| {
+ // let mut discovery_handler_status_seq = Sequence::new();
+ let mut mock = MockDiscoveryOperator::default();
+ mock.expect_get_discovery_handler_map()
+ .returning(move || discovery_handler_map_clone.clone());
+ mock.expect_get_config()
+ .returning(move || config_clone.clone());
+ mock.expect_get_instance_map()
+ .returning(move || instance_map_clone.clone());
+ mock
+ });
+ let mock = MockDiscoveryOperator::new(discovery_handler_map, config, instance_map);
+ mock
+ }
+
+ // Creates a discovery handler with specified properties and adds it to the RegisteredDiscoveryHandlerMap.
+ pub fn add_discovery_handler_to_map(
+ dh_name: &str,
+ endpoint: &DiscoveryHandlerEndpoint,
+ shared: bool,
+ registered_dh_map: RegisteredDiscoveryHandlerMap,
+ ) {
+ let discovery_handler_details =
+ create_discovery_handler_details(dh_name, endpoint.clone(), shared);
+ // Add discovery handler to registered discovery handler map
+ let dh_details_map = match registered_dh_map.lock().unwrap().clone().get_mut(dh_name) {
+ Some(dh_details_map) => {
+ dh_details_map.insert(endpoint.clone(), discovery_handler_details);
+ dh_details_map.clone()
+ }
+ None => {
+ let mut dh_details_map = HashMap::new();
+ dh_details_map.insert(endpoint.clone(), discovery_handler_details);
+ dh_details_map
+ }
+ };
+ registered_dh_map
+ .lock()
+ .unwrap()
+ .insert(dh_name.to_string(), dh_details_map);
+ }
+
+ fn create_discovery_handler_details(
+ name: &str,
+ endpoint: DiscoveryHandlerEndpoint,
+ shared: bool,
+ ) -> DiscoveryDetails {
+ let (close_discovery_handler_connection, _) = broadcast::channel(2);
+ DiscoveryDetails {
+ name: name.to_string(),
+ endpoint,
+ shared,
+ close_discovery_handler_connection: close_discovery_handler_connection.clone(),
+ connectivity_status: DiscoveryHandlerStatus::Waiting,
+ }
+ }
+
+ fn setup_test_do_discover() -> (MockDiscoveryOperator, RegisteredDiscoveryHandlerMap) {
+ let discovery_handler_map = Arc::new(std::sync::Mutex::new(HashMap::new()));
+ add_discovery_handler_to_map(
+ "debugEcho",
+ &DiscoveryHandlerEndpoint::Uds("socket.sock".to_string()),
+ false,
+ discovery_handler_map.clone(),
+ );
+
+ // Build discovery operator
+ let path_to_config = "../test/yaml/config-a.yaml";
+ let config_yaml = std::fs::read_to_string(path_to_config).expect("Unable to read file");
+ let config: KubeAkriConfig = serde_yaml::from_str(&config_yaml).unwrap();
+ let discovery_operator = create_mock_discovery_operator(
+ discovery_handler_map.clone(),
+ config,
+ Arc::new(tokio::sync::Mutex::new(HashMap::new())),
+ );
+ (discovery_operator, discovery_handler_map)
+ }
+
+ #[test]
+ fn test_generate_instance_digest() {
+ let mut mock_env_var_a = MockEnvVarQuery::new();
+ mock_env_var_a
+ .expect_get_env_var()
+ .returning(|_| Ok("node-a".to_string()));
+ let id = "video1";
+ let first_unshared_video_digest =
+ inner_generate_instance_digest(id, false, &mock_env_var_a);
+ let first_shared_video_digest = inner_generate_instance_digest(id, true, &mock_env_var_a);
+ let mut mock_env_var_b = MockEnvVarQuery::new();
+ mock_env_var_b
+ .expect_get_env_var()
+ .returning(|_| Ok("node-b".to_string()));
+ let second_unshared_video_digest =
+ inner_generate_instance_digest(id, false, &mock_env_var_b);
+ let second_shared_video_digest = inner_generate_instance_digest(id, true, &mock_env_var_b);
+ // unshared instances visible to different nodes should NOT have the same digest
+ assert_ne!(first_unshared_video_digest, second_unshared_video_digest);
+ // shared instances visible to different nodes should have the same digest
+ assert_eq!(first_shared_video_digest, second_shared_video_digest);
+ }
+
+ #[tokio::test]
+ async fn test_stop_all_discovery() {
+ let dh_name = "debugEcho";
+ let discovery_handler_map = Arc::new(std::sync::Mutex::new(HashMap::new()));
+ let endpoint1 = DiscoveryHandlerEndpoint::Uds("socket.sock".to_string());
+ add_discovery_handler_to_map(dh_name, &endpoint1, false, discovery_handler_map.clone());
+ let mut close_discovery_handler_connection_receiver1 = discovery_handler_map
+ .lock()
+ .unwrap()
+ .get(dh_name)
+ .unwrap()
+ .get(&endpoint1)
+ .unwrap()
+ .close_discovery_handler_connection
+ .subscribe();
+ let endpoint2 = DiscoveryHandlerEndpoint::Uds("socket2.sock".to_string());
+ add_discovery_handler_to_map(dh_name, &endpoint2, false, discovery_handler_map.clone());
+ let mut close_discovery_handler_connection_receiver2 = discovery_handler_map
+ .lock()
+ .unwrap()
+ .get(dh_name)
+ .unwrap()
+ .get(&endpoint2)
+ .unwrap()
+ .close_discovery_handler_connection
+ .subscribe();
+ let path_to_config = "../test/yaml/config-a.yaml";
+ let config_yaml = std::fs::read_to_string(path_to_config).expect("Unable to read file");
+ let config: KubeAkriConfig = serde_yaml::from_str(&config_yaml).unwrap();
+ let discovery_operator = Arc::new(DiscoveryOperator::new(
+ discovery_handler_map,
+ config,
+ Arc::new(tokio::sync::Mutex::new(HashMap::new())),
+ ));
+ tokio::spawn(async move {
+ discovery_operator.stop_all_discovery().await;
+ });
+ assert!(close_discovery_handler_connection_receiver1
+ .recv()
+ .await
+ .is_ok());
+ assert!(close_discovery_handler_connection_receiver2
+ .recv()
+ .await
+ .is_ok());
+ }
+
+ #[tokio::test]
+ async fn test_start_discovery_termination() {
+ let _ = env_logger::builder().is_test(true).try_init();
+ let (mut mock_discovery_operator, discovery_handler_map) = setup_test_do_discover();
+ let (marked_offline_sender, mut marked_offline_receiver) =
+ tokio::sync::broadcast::channel(1);
+ mock_discovery_operator
+ .expect_get_stream()
+ .returning(|_| None);
+ mock_discovery_operator
+ .expect_mark_offline_or_deregister_discovery_handler()
+ .withf(move |endpoint: &DiscoveryHandlerEndpoint| {
+ endpoint == &DiscoveryHandlerEndpoint::Uds("socket.sock".to_string())
+ })
+ .returning(move |_| {
+ marked_offline_sender.clone().send(()).unwrap();
+ Ok(false)
+ });
+ mock_discovery_operator
+ .expect_delete_offline_instances()
+ .times(1)
+ .returning(move |_| Ok(()));
+ let stop_dh_discovery_sender = discovery_handler_map
+ .lock()
+ .unwrap()
+ .get_mut("debugEcho")
+ .unwrap()
+ .clone()
+ .get(&DiscoveryHandlerEndpoint::Uds("socket.sock".to_string()))
+ .unwrap()
+ .clone()
+ .close_discovery_handler_connection
+ .clone();
+ mock_discovery_operator
+ .expect_stop_all_discovery()
+ .times(1)
+ .returning(move || {
+ stop_dh_discovery_sender.clone().send(()).unwrap();
+ });
+ let (new_dh_sender, _) = broadcast::channel(2);
+ let (stop_all_discovery_sender, _) = broadcast::channel(2);
+ let (mut finished_discovery_sender, mut finished_discovery_receiver) =
+ tokio::sync::mpsc::channel(2);
+ let thread_new_dh_sender = new_dh_sender.clone();
+ let thread_stop_all_discovery_sender = stop_all_discovery_sender.clone();
+ let mock_kube_interface: Arc> =
+ Arc::new(Box::new(MockKubeInterface::new()));
+ let handle = tokio::spawn(async move {
+ start_discovery::start_discovery(
+ mock_discovery_operator,
+ thread_new_dh_sender,
+ thread_stop_all_discovery_sender,
+ &mut finished_discovery_sender,
+ mock_kube_interface,
+ )
+ .await
+ .unwrap();
+ });
+
+ // Wait until do_discovery has gotten to point the DH marked offline
+ marked_offline_receiver.recv().await.unwrap();
+ stop_all_discovery_sender.send(()).unwrap();
+ finished_discovery_receiver.recv().await.unwrap();
+ // Make sure that all threads have finished
+ handle.await.unwrap();
+ }
+
+ // Test that DH is connected to on second try getting stream and
+ // that connectivity status is changed from Waiting -> Active -> Waiting again
+ // when a successful connection is made and completed.
+ #[tokio::test]
+ async fn test_do_discover_completed_internal_connection() {
+ let _ = env_logger::builder().is_test(true).try_init();
+ let (mut mock_discovery_operator, _) = setup_test_do_discover();
+ let mut get_stream_seq = Sequence::new();
+ // First time cannot get stream and is marked offline
+ mock_discovery_operator
+ .expect_get_stream()
+ .times(1)
+ .returning(|_| None)
+ .in_sequence(&mut get_stream_seq);
+ mock_discovery_operator
+ .expect_mark_offline_or_deregister_discovery_handler()
+ .withf(move |endpoint: &DiscoveryHandlerEndpoint| {
+ endpoint == &DiscoveryHandlerEndpoint::Uds("socket.sock".to_string())
+ })
+ .times(1)
+ .returning(|_| Ok(false));
+ // Second time successfully get stream
+ let (_, rx) = mpsc::channel(2);
+ let stream_type = Some(StreamType::Embedded(rx));
+ mock_discovery_operator
+ .expect_get_stream()
+ .times(1)
+ .return_once(move |_| stream_type)
+ .in_sequence(&mut get_stream_seq);
+ // Make sure discovery handler is marked as Active
+ let mut discovery_handler_status_seq = Sequence::new();
+ mock_discovery_operator
+ .expect_set_discovery_handler_connectivity_status()
+ .withf(
+ move |endpoint: &DiscoveryHandlerEndpoint,
+ connectivity_status: &DiscoveryHandlerStatus| {
+ endpoint == &DiscoveryHandlerEndpoint::Uds("socket.sock".to_string())
+ && connectivity_status == &DiscoveryHandlerStatus::Active
+ },
+ )
+ .times(1)
+ .returning(|_, _| ())
+ .in_sequence(&mut discovery_handler_status_seq);
+ // Discovery should be initiated
+ mock_discovery_operator
+ .expect_internal_do_discover()
+ .times(1)
+ .returning(|_, _, _| Ok(()));
+ // Make sure after discovery is complete that the DH is marked Online again
+ mock_discovery_operator
+ .expect_set_discovery_handler_connectivity_status()
+ .withf(
+ move |endpoint: &DiscoveryHandlerEndpoint,
+ connectivity_status: &DiscoveryHandlerStatus| {
+ endpoint == &DiscoveryHandlerEndpoint::Uds("socket.sock".to_string())
+ && connectivity_status == &DiscoveryHandlerStatus::Waiting
+ },
+ )
+ .times(1)
+ .returning(|_, _| ())
+ .in_sequence(&mut discovery_handler_status_seq);
+ let mock_kube_interface: Arc> =
+ Arc::new(Box::new(MockKubeInterface::new()));
+ start_discovery::do_discover(Arc::new(mock_discovery_operator), mock_kube_interface)
+ .await
+ .unwrap();
+ }
+
+ #[tokio::test]
+ async fn test_handle_discovery_results() {
+ let _ = env_logger::builder().is_test(true).try_init();
+ // Set node name for generating instance id
+ std::env::set_var("AGENT_NODE_NAME", "node-a");
+ let mock_kube_interface: Arc> =
+ Arc::new(Box::new(MockKubeInterface::new()));
+ let discovery_handler_map: RegisteredDiscoveryHandlerMap =
+ Arc::new(std::sync::Mutex::new(HashMap::new()));
+ let path_to_config = "../test/yaml/config-a.yaml";
+ let config_yaml = std::fs::read_to_string(path_to_config).expect("Unable to read file");
+ let config: KubeAkriConfig = serde_yaml::from_str(&config_yaml).unwrap();
+ let config_name = config.metadata.name.clone();
+ INSTANCE_COUNT_METRIC
+ .with_label_values(&[&config_name, "true"])
+ .set(0);
+ let device1 = Device {
+ id: "device1".to_string(),
+ properties: HashMap::new(),
+ mounts: Vec::default(),
+ device_specs: Vec::default(),
+ };
+ let device2 = Device {
+ id: "device2".to_string(),
+ properties: HashMap::new(),
+ mounts: Vec::default(),
+ device_specs: Vec::default(),
+ };
+ let discovery_results: Vec = vec![device1, device2];
+ let discovery_operator = Arc::new(DiscoveryOperator::new(
+ discovery_handler_map,
+ config,
+ Arc::new(tokio::sync::Mutex::new(HashMap::new())),
+ ));
+ let mut mock_device_plugin_builder = MockDevicePluginBuilderInterface::new();
+ mock_device_plugin_builder
+ .expect_build_device_plugin()
+ .times(2)
+ .returning(move |_, _, _, _, _| Ok(()));
+ discovery_operator
+ .handle_discovery_results(
+ mock_kube_interface,
+ discovery_results,
+ true,
+ Box::new(mock_device_plugin_builder),
+ )
+ .await
+ .unwrap();
+
+ assert_eq!(
+ INSTANCE_COUNT_METRIC
+ .with_label_values(&[&config_name, "true"])
+ .get(),
+ 2
+ );
+ }
+
+ // Checks either that InstanceConnectivityStatus changed to expected value until success or exceeded tries
+ // or that all instances have been deleted from map.
+ // Sleep between tries to give update_instance_connectivity_status the chance chance to grab mutex InstanceMap.
+ async fn check_status_or_empty_loop(
+ status: InstanceConnectivityStatus,
+ equality: bool,
+ instance_map: InstanceMap,
+ check_empty: bool,
+ ) {
+ let mut keep_looping = false;
+ let mut map_is_empty = false;
+ let tries: i8 = 5;
+ for _x in 0..tries {
+ println!("try number {}", _x);
+ keep_looping = false;
+ tokio::time::delay_for(Duration::from_millis(100)).await;
+ let unwrapped_instance_map = instance_map.lock().await.clone();
+ if check_empty && unwrapped_instance_map.is_empty() {
+ map_is_empty = true;
+ break;
+ }
+ for (_, instance_info) in unwrapped_instance_map {
+ if instance_info.connectivity_status != status && equality {
+ keep_looping = true;
+ }
+ if instance_info.connectivity_status == status && !equality {
+ keep_looping = true;
+ }
+ }
+ if !keep_looping {
+ break;
+ }
+ }
+ if keep_looping {
+ panic!(
+ "failed to assert that all instances had status equal T/F: [{}] to status [{:?}]",
+ equality, status
+ );
+ }
+ if check_empty && !map_is_empty {
+ panic!("instances were not cleared from map");
+ }
+ }
+
+ #[tokio::test]
+ async fn test_delete_offline_instances() {
+ let _ = env_logger::builder().is_test(true).try_init();
+ let path_to_config = "../test/yaml/config-a.yaml";
+ let config_yaml = std::fs::read_to_string(path_to_config).expect("Unable to read file");
+ let config: KubeAkriConfig = serde_yaml::from_str(&config_yaml).unwrap();
+ let mut list_and_watch_message_receivers = Vec::new();
+ let discovery_handler_map: RegisteredDiscoveryHandlerMap =
+ Arc::new(std::sync::Mutex::new(HashMap::new()));
+ let mut visible_discovery_results = Vec::new();
+
+ // Assert no action (to delete instances by mock kube interface) is taken for all online instances
+ let instance_map: InstanceMap = build_instance_map(
+ &config,
+ &mut visible_discovery_results,
+ &mut list_and_watch_message_receivers,
+ InstanceConnectivityStatus::Online,
+ )
+ .await;
+ let mock = MockKubeInterface::new();
+ let discovery_operator = Arc::new(DiscoveryOperator::new(
+ discovery_handler_map.clone(),
+ config.clone(),
+ instance_map,
+ ));
+ discovery_operator
+ .delete_offline_instances(Arc::new(Box::new(mock)))
+ .await
+ .unwrap();
+
+ // Assert no action (to delete instances by mock kube interface) is taken for instances offline for less than grace period
+ let mock_now = Instant::now();
+ MockClock::advance(Duration::from_secs(30));
+ let instance_map: InstanceMap = build_instance_map(
+ &config,
+ &mut visible_discovery_results,
+ &mut list_and_watch_message_receivers,
+ InstanceConnectivityStatus::Offline(mock_now),
+ )
+ .await;
+ let mock = MockKubeInterface::new();
+ let discovery_operator = Arc::new(DiscoveryOperator::new(
+ discovery_handler_map.clone(),
+ config.clone(),
+ instance_map,
+ ));
+ discovery_operator
+ .delete_offline_instances(Arc::new(Box::new(mock)))
+ .await
+ .unwrap();
+
+ // Assert that all instances that have been offline for more than 5 minutes are deleted
+ let mock_now = Instant::now();
+ MockClock::advance(Duration::from_secs(301));
+ let instance_map: InstanceMap = build_instance_map(
+ &config,
+ &mut visible_discovery_results,
+ &mut list_and_watch_message_receivers,
+ InstanceConnectivityStatus::Offline(mock_now),
+ )
+ .await;
+ let mut mock = MockKubeInterface::new();
+ mock.expect_delete_instance()
+ .times(2)
+ .returning(move |_, _| Ok(()));
+ let discovery_operator = Arc::new(DiscoveryOperator::new(
+ discovery_handler_map.clone(),
+ config.clone(),
+ instance_map.clone(),
+ ));
+ discovery_operator
+ .delete_offline_instances(Arc::new(Box::new(mock)))
+ .await
+ .unwrap();
+ // Make sure all instances are deleted from map. Note, first 3 arguments are ignored.
+ check_status_or_empty_loop(InstanceConnectivityStatus::Online, true, instance_map, true)
+ .await;
+ }
+
+ // 1: InstanceConnectivityStatus of all instances that go offline is changed from Online to Offline
+ // 2: InstanceConnectivityStatus of shared instances that come back online in under 5 minutes is changed from Offline to Online
+ // 3: InstanceConnectivityStatus of unshared instances that come back online before next periodic discovery is changed from Offline to Online
+ #[tokio::test(core_threads = 2)]
+ async fn test_update_instance_connectivity_status_factory() {
+ let _ = env_logger::builder().is_test(true).try_init();
+ let path_to_config = "../test/yaml/config-a.yaml";
+ let config_yaml = std::fs::read_to_string(path_to_config).expect("Unable to read file");
+ let config: KubeAkriConfig = serde_yaml::from_str(&config_yaml).unwrap();
+ let config_name = config.metadata.name.clone();
+ let mut list_and_watch_message_receivers = Vec::new();
+ let mut visible_discovery_results = Vec::new();
+ let discovery_handler_map: RegisteredDiscoveryHandlerMap =
+ Arc::new(std::sync::Mutex::new(HashMap::new()));
+ let discovery_handler_map_clone = discovery_handler_map.clone();
+ // set environment variable to set whether debug echo instances are shared
+ let mut mock_env_var_shared = MockEnvVarQuery::new();
+ mock_env_var_shared
+ .expect_get_env_var()
+ .returning(|_| Ok("false".to_string()));
+ inner_register_embedded_discovery_handlers(
+ discovery_handler_map_clone,
+ &mock_env_var_shared,
+ )
+ .unwrap();
+
+ //
+ // 1: Assert that InstanceConnectivityStatus of non local instances that are no longer visible is changed to Offline
+ //
+ let instance_map: InstanceMap = build_instance_map(
+ &config,
+ &mut visible_discovery_results,
+ &mut list_and_watch_message_receivers,
+ InstanceConnectivityStatus::Online,
+ )
+ .await;
+ let shared = true;
+ run_update_instance_connectivity_status(
+ config.clone(),
+ HashMap::new(),
+ shared,
+ instance_map.clone(),
+ discovery_handler_map.clone(),
+ MockKubeInterface::new(),
+ )
+ .await;
+
+ // Check that no instances are still online
+ check_status_or_empty_loop(
+ InstanceConnectivityStatus::Online,
+ false,
+ instance_map,
+ false,
+ )
+ .await;
+
+ //
+ // 2: Assert that InstanceConnectivityStatus of shared instances that come back online in <5 mins is changed to Online
+ //
+ let mock_now = Instant::now();
+ MockClock::advance(Duration::from_secs(30));
+ let instance_map: InstanceMap = build_instance_map(
+ &config,
+ &mut visible_discovery_results,
+ &mut list_and_watch_message_receivers,
+ InstanceConnectivityStatus::Offline(mock_now),
+ )
+ .await;
+ let currently_visible_instances: HashMap = visible_discovery_results
+ .iter()
+ .map(|device| {
+ let instance_name = get_device_instance_name(&device.id, &config_name);
+ (instance_name, device.clone())
+ })
+ .collect();
+ let shared = true;
+ run_update_instance_connectivity_status(
+ config.clone(),
+ currently_visible_instances.clone(),
+ shared,
+ instance_map.clone(),
+ discovery_handler_map.clone(),
+ MockKubeInterface::new(),
+ )
+ .await;
+
+ // Check that all instances marked online
+ check_status_or_empty_loop(
+ InstanceConnectivityStatus::Online,
+ true,
+ instance_map,
+ false,
+ )
+ .await;
+
+ //
+ // 3: Assert that shared instances that are offline for more than 5 minutes are removed from the instance map
+ //
+ let mock_now = Instant::now();
+ MockClock::advance(Duration::from_secs(301));
+ let instance_map: InstanceMap = build_instance_map(
+ &config,
+ &mut visible_discovery_results,
+ &mut list_and_watch_message_receivers,
+ InstanceConnectivityStatus::Offline(mock_now),
+ )
+ .await;
+ let mut mock = MockKubeInterface::new();
+ mock.expect_delete_instance()
+ .times(2)
+ .returning(move |_, _| Ok(()));
+ let shared = true;
+ run_update_instance_connectivity_status(
+ config.clone(),
+ HashMap::new(),
+ shared,
+ instance_map.clone(),
+ discovery_handler_map.clone(),
+ mock,
+ )
+ .await;
+ // Make sure all instances are deleted from map. Note, first 3 arguments are ignored.
+ check_status_or_empty_loop(InstanceConnectivityStatus::Online, true, instance_map, true)
+ .await;
+
+ //
+ // 4: Assert that local devices that go offline are removed from the instance map
+ //
+ let mut mock = MockKubeInterface::new();
+ mock.expect_delete_instance()
+ .times(2)
+ .returning(move |_, _| Ok(()));
+
+ let instance_map: InstanceMap = build_instance_map(
+ &config,
+ &mut visible_discovery_results,
+ &mut list_and_watch_message_receivers,
+ InstanceConnectivityStatus::Online,
+ )
+ .await;
+ let shared = false;
+ run_update_instance_connectivity_status(
+ config,
+ HashMap::new(),
+ shared,
+ instance_map.clone(),
+ discovery_handler_map.clone(),
+ mock,
+ )
+ .await;
+ // Make sure all instances are deleted from map. Note, first 3 arguments are ignored.
+ check_status_or_empty_loop(InstanceConnectivityStatus::Online, true, instance_map, true)
+ .await;
+ }
+
+ async fn run_update_instance_connectivity_status(
+ config: KubeAkriConfig,
+ currently_visible_instances: HashMap,
+ shared: bool,
+ instance_map: InstanceMap,
+ discovery_handler_map: RegisteredDiscoveryHandlerMap,
+ mock: MockKubeInterface,
+ ) {
+ let discovery_operator = Arc::new(DiscoveryOperator::new(
+ discovery_handler_map,
+ config,
+ instance_map.clone(),
+ ));
+ discovery_operator
+ .update_instance_connectivity_status(
+ Arc::new(Box::new(mock)),
+ currently_visible_instances,
+ shared,
+ )
+ .await
+ .unwrap();
+ }
+
+ fn setup_non_mocked_dh(
+ dh_name: &str,
+ endpoint: &DiscoveryHandlerEndpoint,
+ ) -> DiscoveryOperator {
+ let path_to_config = "../test/yaml/config-a.yaml";
+ let config_yaml = std::fs::read_to_string(path_to_config).expect("Unable to read file");
+ let config: KubeAkriConfig = serde_yaml::from_str(&config_yaml).unwrap();
+ let discovery_handler_map = Arc::new(std::sync::Mutex::new(HashMap::new()));
+ add_discovery_handler_to_map(dh_name, endpoint, false, discovery_handler_map.clone());
+ DiscoveryOperator::new(
+ discovery_handler_map,
+ config,
+ Arc::new(tokio::sync::Mutex::new(HashMap::new())),
+ )
+ }
+
+ #[tokio::test]
+ async fn test_set_discovery_handler_connectivity_status() {
+ let _ = env_logger::builder().is_test(true).try_init();
+ let discovery_handler_name = "debugEcho";
+ let endpoint = DiscoveryHandlerEndpoint::Uds("socket.sock".to_string());
+ let discovery_operator = setup_non_mocked_dh(discovery_handler_name, &endpoint);
+ // Test that an online discovery handler is marked Active
+ discovery_operator
+ .set_discovery_handler_connectivity_status(&endpoint, DiscoveryHandlerStatus::Active);
+ assert_eq!(
+ discovery_operator
+ .discovery_handler_map
+ .lock()
+ .unwrap()
+ .get_mut(discovery_handler_name)
+ .unwrap()
+ .clone()
+ .get(&endpoint)
+ .unwrap()
+ .clone()
+ .connectivity_status,
+ DiscoveryHandlerStatus::Active
+ );
+ }
+
+ #[tokio::test]
+ async fn test_mark_offline_or_deregister_discovery_handler() {
+ let _ = env_logger::builder().is_test(true).try_init();
+ let discovery_handler_name = "debugEcho";
+ let endpoint = DiscoveryHandlerEndpoint::Uds("socket.sock".to_string());
+ let discovery_operator = setup_non_mocked_dh(discovery_handler_name, &endpoint);
+ // Test that an online discovery handler is marked offline
+ assert_eq!(
+ discovery_operator
+ .mark_offline_or_deregister_discovery_handler(&endpoint)
+ .await
+ .unwrap(),
+ false
+ );
+ if let DiscoveryHandlerStatus::Offline(_) = discovery_operator
+ .discovery_handler_map
+ .lock()
+ .unwrap()
+ .get_mut(discovery_handler_name)
+ .unwrap()
+ .clone()
+ .get(&endpoint)
+ .unwrap()
+ .clone()
+ .connectivity_status
+ {
+ // expected
+ } else {
+ panic!("DiscoveryHandlerStatus should be changed to offline");
+ }
+ // Test that an offline discovery handler IS NOT deregistered if the time has not passed
+ assert_eq!(
+ discovery_operator
+ .mark_offline_or_deregister_discovery_handler(&endpoint)
+ .await
+ .unwrap(),
+ false
+ );
+
+ // Test that an offline discovery handler IS deregistered if the time has passed
+ let mock_now = Instant::now();
+ MockClock::advance(Duration::from_secs(301));
+ discovery_operator
+ .discovery_handler_map
+ .lock()
+ .unwrap()
+ .get_mut(discovery_handler_name)
+ .unwrap()
+ .get_mut(&endpoint)
+ .unwrap()
+ .connectivity_status = DiscoveryHandlerStatus::Offline(mock_now);
+ assert_eq!(
+ discovery_operator
+ .mark_offline_or_deregister_discovery_handler(&endpoint)
+ .await
+ .unwrap(),
+ true
+ );
+ }
+
+ #[tokio::test]
+ async fn test_get_stream_embedded() {
+ let _ = env_logger::builder().is_test(true).try_init();
+ std::env::set_var(super::super::constants::ENABLE_DEBUG_ECHO_LABEL, "yes");
+ let path_to_config = "../test/yaml/config-a.yaml";
+ let config_yaml = std::fs::read_to_string(path_to_config).expect("Unable to read file");
+ let config: KubeAkriConfig = serde_yaml::from_str(&config_yaml).unwrap();
+ let discovery_handler_map = Arc::new(std::sync::Mutex::new(HashMap::new()));
+ let endpoint = DiscoveryHandlerEndpoint::Embedded;
+ let dh_name = akri_debug_echo::DISCOVERY_HANDLER_NAME.to_string();
+ add_discovery_handler_to_map(&dh_name, &endpoint, false, discovery_handler_map.clone());
+ let discovery_operator = DiscoveryOperator::new(
+ discovery_handler_map,
+ config,
+ Arc::new(tokio::sync::Mutex::new(HashMap::new())),
+ );
+ // test embedded debugEcho socket
+ if let Some(StreamType::Embedded(_)) = discovery_operator
+ .get_stream(&DiscoveryHandlerEndpoint::Embedded)
+ .await
+ {
+ // expected
+ } else {
+ panic!("expected internal stream");
+ }
+ }
+
+ async fn setup_and_run_mock_discovery_handler(
+ endpoint: &str,
+ endpoint_dir: &str,
+ dh_endpoint: &DiscoveryHandlerEndpoint,
+ return_error: bool,
+ ) -> DiscoveryOperator {
+ let discovery_operator = setup_non_mocked_dh("mockName", dh_endpoint);
+ // Start mock DH, specifying that it should successfully run
+ let _dh_server_thread_handle = mock_discovery_handler::run_mock_discovery_handler(
+ endpoint_dir,
+ endpoint,
+ return_error,
+ Vec::new(),
+ )
+ .await;
+ // Make sure registration server has started
+ akri_shared::uds::unix_stream::try_connect(&endpoint)
+ .await
+ .unwrap();
+ discovery_operator
+ }
+
+ #[tokio::test]
+ async fn test_get_stream_no_dh() {
+ let (_, endpoint) =
+ mock_discovery_handler::get_mock_discovery_handler_dir_and_endpoint("mock.sock");
+ let dh_endpoint = DiscoveryHandlerEndpoint::Uds(endpoint.to_string());
+ let discovery_operator = setup_non_mocked_dh("mock", &dh_endpoint);
+ // Should not be able to get stream if DH is not running
+ assert!(discovery_operator.get_stream(&dh_endpoint).await.is_none());
+ }
+
+ #[tokio::test]
+ async fn test_get_stream_error() {
+ // Start mock DH, specifying that it should return an error
+ let return_error = true;
+ let (endpoint_dir, endpoint) =
+ mock_discovery_handler::get_mock_discovery_handler_dir_and_endpoint("mock.sock");
+ let dh_endpoint = DiscoveryHandlerEndpoint::Uds(endpoint.to_string());
+ let discovery_operator = setup_and_run_mock_discovery_handler(
+ &endpoint,
+ &endpoint_dir,
+ &dh_endpoint,
+ return_error,
+ )
+ .await;
+ // Assert that get_stream returns none if the DH returns error
+ assert!(discovery_operator.get_stream(&dh_endpoint).await.is_none());
+ }
+
+ #[tokio::test]
+ async fn test_get_stream_external_success() {
+ // Start mock DH, specifying that it should NOT return an error
+ let return_error = false;
+ let (endpoint_dir, endpoint) =
+ mock_discovery_handler::get_mock_discovery_handler_dir_and_endpoint("mock.sock");
+ let dh_endpoint = DiscoveryHandlerEndpoint::Uds(endpoint.to_string());
+ let discovery_operator = setup_and_run_mock_discovery_handler(
+ &endpoint,
+ &endpoint_dir,
+ &dh_endpoint,
+ return_error,
+ )
+ .await;
+ if let Some(StreamType::External(mut receiver)) =
+ discovery_operator.get_stream(&dh_endpoint).await
+ {
+ // MockDiscoveryHandler returns an empty array of devices
+ assert_eq!(
+ receiver.get_message().await.unwrap().unwrap().devices.len(),
+ 0
+ );
+ } else {
+ panic!("expected external stream");
+ }
+ }
+}
diff --git a/agent/src/util/embedded_discovery_handlers.rs b/agent/src/util/embedded_discovery_handlers.rs
new file mode 100644
index 000000000..2e0427fd5
--- /dev/null
+++ b/agent/src/util/embedded_discovery_handlers.rs
@@ -0,0 +1,138 @@
+use akri_debug_echo::discovery_handler::DebugEchoDiscoveryDetails;
+use akri_discovery_utils::discovery::{
+ v0::discovery_handler_server::DiscoveryHandler, DiscoverStream,
+};
+#[cfg(any(test, feature = "onvif-feat"))]
+use akri_onvif::discovery_handler::OnvifDiscoveryDetails;
+#[cfg(any(test, feature = "opcua-feat"))]
+use akri_opcua::discovery_handler::OpcuaDiscoveryDetails;
+use akri_shared::{
+ akri::configuration::DiscoveryHandlerInfo,
+ os::env_var::{ActualEnvVarQuery, EnvVarQuery},
+};
+#[cfg(any(test, feature = "udev-feat"))]
+use akri_udev::discovery_handler::UdevDiscoveryDetails;
+use anyhow::Error;
+use log::trace;
+
+/// Returns the appropriate embedded DiscoveryHandler as determined by the deserialized discovery_details string.
+pub fn get_discovery_handler(
+ discovery_handler_info: &DiscoveryHandlerInfo,
+) -> Result>, Error> {
+ let query_var_set = ActualEnvVarQuery {};
+ inner_get_discovery_handler(discovery_handler_info, &query_var_set)
+}
+
+fn inner_get_discovery_handler(
+ discovery_handler_info: &DiscoveryHandlerInfo,
+ query: &impl EnvVarQuery,
+) -> Result>, Error> {
+ trace!(
+ "inner_get_discovery_handler - for DiscoveryHandlerInfo {:?}",
+ discovery_handler_info
+ );
+ // Determine whether it is an embedded discovery handler
+ match discovery_handler_info.name.as_str() {
+ #[cfg(any(test, feature = "onvif-feat"))]
+ akri_onvif::DISCOVERY_HANDLER_NAME => {
+ trace!("here in onvif");
+ let _discovery_handler_config: OnvifDiscoveryDetails = serde_yaml::from_str(&discovery_handler_info.discovery_details).map_err(|e| anyhow::format_err!("ONVIF Configuration discovery details improperly configured with error {:?}", e))?;
+ Ok(Box::new(
+ akri_onvif::discovery_handler::DiscoveryHandlerImpl::new(None),
+ ))
+ }
+ #[cfg(any(test, feature = "udev-feat"))]
+ akri_udev::DISCOVERY_HANDLER_NAME => {
+ let _discovery_handler_config: UdevDiscoveryDetails = serde_yaml::from_str(&discovery_handler_info.discovery_details).map_err(|e| anyhow::format_err!("udev Configuration discovery details improperly configured with error {:?}", e))?;
+ Ok(Box::new(
+ akri_udev::discovery_handler::DiscoveryHandlerImpl::new(None),
+ ))
+ }
+ #[cfg(any(test, feature = "opcua-feat"))]
+ akri_opcua::DISCOVERY_HANDLER_NAME => {
+ let _discovery_handler_config: OpcuaDiscoveryDetails = serde_yaml::from_str(&discovery_handler_info.discovery_details).map_err(|e| anyhow::format_err!("OPC UA Configuration discovery details improperly configured with error {:?}", e))?;
+ Ok(Box::new(
+ akri_opcua::discovery_handler::DiscoveryHandlerImpl::new(None),
+ ))
+ }
+ akri_debug_echo::DISCOVERY_HANDLER_NAME => {
+ match query.get_env_var(super::constants::ENABLE_DEBUG_ECHO_LABEL) {
+ Ok(_) => {
+ let _discovery_handler_config: DebugEchoDiscoveryDetails = serde_yaml::from_str(&discovery_handler_info.discovery_details).map_err(|e| anyhow::format_err!("debug echo Configuration discovery details improperly configured with error {:?}", e))?;
+ Ok(Box::new(
+ akri_debug_echo::discovery_handler::DiscoveryHandlerImpl::new(None)))
+ },
+ _ => Err(anyhow::format_err!("Debug echo discovery handler not configured")),
+ }
+ }
+ _ => Err(anyhow::format_err!(
+ "No embedded discovery handler found for configuration with discovery handler info {:?}",
+ discovery_handler_info
+ )),
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use akri_shared::{akri::configuration::DiscoveryHandlerInfo, os::env_var::MockEnvVarQuery};
+ use std::env::VarError;
+
+ #[test]
+ fn test_inner_get_discovery_handler() {
+ let _ = env_logger::builder().is_test(true).try_init();
+ let mock_query = MockEnvVarQuery::new();
+ let deserialized = serde_json::from_str::(
+ r#"{"name":"onvif", "discoveryDetails":"{}"}"#,
+ )
+ .unwrap();
+ assert!(inner_get_discovery_handler(&deserialized, &mock_query).is_ok());
+
+ let udev_yaml = r#"
+ name: udev
+ discoveryDetails: |+
+ udevRules: []
+ "#;
+ let deserialized: DiscoveryHandlerInfo = serde_yaml::from_str(&udev_yaml).unwrap();
+ assert!(inner_get_discovery_handler(&deserialized, &mock_query).is_ok());
+
+ let yaml = r#"
+ name: opcua
+ discoveryDetails: |+
+ opcuaDiscoveryMethod:
+ standard: {}
+ "#;
+ let deserialized: DiscoveryHandlerInfo = serde_yaml::from_str(&yaml).unwrap();
+ assert!(inner_get_discovery_handler(&deserialized, &mock_query).is_ok());
+
+ let deserialized = serde_json::from_str::(
+ r#"{"name":"random", "discoveryDetails":"some details"}"#,
+ )
+ .unwrap();
+ assert!(inner_get_discovery_handler(&deserialized, &mock_query).is_err());
+ }
+
+ #[tokio::test]
+ async fn test_factory_for_debug_echo() {
+ let debug_echo_yaml = r#"
+ discoveryHandler:
+ name: debugEcho
+ discoveryDetails: |+
+ descriptions:
+ - "foo1"
+ "#;
+ let deserialized: DiscoveryHandlerInfo = serde_yaml::from_str(&debug_echo_yaml).unwrap();
+ // Test that errors without environment var set
+ let mut mock_query_without_var_set = MockEnvVarQuery::new();
+ mock_query_without_var_set
+ .expect_get_env_var()
+ .returning(|_| Err(VarError::NotPresent));
+ assert!(inner_get_discovery_handler(&deserialized, &mock_query_without_var_set,).is_err());
+ // Test that succeeds when env var set
+ let mut mock_query_with_var_set = MockEnvVarQuery::new();
+ mock_query_with_var_set
+ .expect_get_env_var()
+ .returning(|_| Ok("1".to_string()));
+ assert!(inner_get_discovery_handler(&deserialized, &mock_query_with_var_set).is_ok());
+ }
+}
diff --git a/agent/src/util/mod.rs b/agent/src/util/mod.rs
index cdc5f45ef..44b2bed80 100644
--- a/agent/src/util/mod.rs
+++ b/agent/src/util/mod.rs
@@ -1,6 +1,11 @@
pub mod config_action;
pub mod constants;
pub mod crictl_containers;
+mod device_plugin_builder;
mod device_plugin_service;
+pub mod discovery_operator;
+pub mod embedded_discovery_handlers;
+pub mod registration;
pub mod slot_reconciliation;
+pub mod streaming_extension;
mod v1beta1;
diff --git a/agent/src/util/registration.rs b/agent/src/util/registration.rs
new file mode 100644
index 000000000..a70cdd109
--- /dev/null
+++ b/agent/src/util/registration.rs
@@ -0,0 +1,468 @@
+use super::constants::{
+ CLOSE_DISCOVERY_HANDLER_CONNECTION_CHANNEL_CAPACITY, ENABLE_DEBUG_ECHO_LABEL,
+};
+use akri_discovery_utils::discovery::v0::{
+ register_discovery_handler_request::EndpointType,
+ registration_server::{Registration, RegistrationServer},
+ Empty, RegisterDiscoveryHandlerRequest,
+};
+use akri_shared::{
+ os::env_var::{ActualEnvVarQuery, EnvVarQuery},
+ uds::unix_stream,
+};
+use futures::TryStreamExt;
+#[cfg(test)]
+use mock_instant::Instant;
+use std::collections::HashMap;
+use std::sync::{Arc, Mutex};
+#[cfg(not(test))]
+use std::time::Instant;
+use tokio::sync::broadcast;
+use tonic::{transport::Server, Request, Response, Status};
+
+/// Map of `DiscoveryHandlers` of the same type (registered with the same name) where key is the endpoint of the
+/// Discovery Handler and value is `DiscoveryDetails`.
+pub type DiscoveryHandlerDetailsMap = HashMap;
+
+/// Map of all registered `DiscoveryHandlers` where key is `DiscoveryHandler` name and value is a map of all
+/// `DiscoveryHandlers` with that name.
+pub type RegisteredDiscoveryHandlerMap =
+ Arc>>;
+
+/// Alias illustrating that `AgentRegistration.new_discovery_handler_sender`, sends the Discovery Handler name of the
+/// newly registered Discovery Handler.
+pub type DiscoveryHandlerName = String;
+
+/// A Discovery Handler's endpoint, distinguished by URI type
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum DiscoveryHandlerEndpoint {
+ /// Embedded means the Discovery Handler is running inside the Agent
+ Embedded,
+ /// Uds means the Discovery Handler is running on a specified unix domain socket
+ Uds(String),
+ /// Network means the Discovery Handler is running at an specified URL
+ Network(String),
+}
+
+/// Describes the connectivity status of a Discovery Handler.
+#[derive(PartialEq, Debug, Clone)]
+pub enum DiscoveryHandlerStatus {
+ /// This discovery handler is currently doing discovery on behalf of the Agent
+ Active,
+ /// This discovery handler is available and waiting for a discover call from the Agent
+ Waiting,
+ /// Not returning discovery results
+ Offline(Instant),
+}
+
+/// Details about a `DiscoveryHandler` and a sender for terminating its clients when needed.
+#[derive(Debug, Clone)]
+pub struct DiscoveryDetails {
+ /// Name of the `DiscoveryHandler`
+ pub name: String,
+ /// Endpoint of the `DiscoveryHandler`
+ pub endpoint: DiscoveryHandlerEndpoint,
+ /// Whether instances discovered by the `DiscoveryHandler` can be shared/seen by multiple nodes.
+ pub shared: bool,
+ /// Channel over which the Registration service tells a DiscoveryOperator client to close a connection with a
+ /// `DiscoveryHandler`, if any. A broadcast channel is used so both the sending and receiving ends can be cloned.
+ pub close_discovery_handler_connection: broadcast::Sender<()>,
+ /// Connection state of the `DiscoveryHandler`.
+ pub connectivity_status: DiscoveryHandlerStatus,
+}
+
+/// This maps the endpoint string and endpoint type of a `RegisterDiscoveryHandlerRequest` into a
+/// `DiscoveryHandlerEndpoint` so as to support embedded `DiscoveryHandlers`.
+pub fn create_discovery_handler_endpoint(
+ endpoint: &str,
+ endpoint_type: EndpointType,
+) -> DiscoveryHandlerEndpoint {
+ match endpoint_type {
+ EndpointType::Network => DiscoveryHandlerEndpoint::Network(endpoint.to_string()),
+ EndpointType::Uds => DiscoveryHandlerEndpoint::Uds(endpoint.to_string()),
+ }
+}
+
+/// Hosts a register service that external Discovery Handlers can call in order to be added to the
+/// RegisteredDiscoveryHandlerMap that is shared with DiscoveryOperators. When a new Discovery Handler is registered, a
+/// message is broadcast to inform any running DiscoveryOperators in case they should use the new Discovery Handler.
+pub struct AgentRegistration {
+ new_discovery_handler_sender: broadcast::Sender,
+ registered_discovery_handlers: RegisteredDiscoveryHandlerMap,
+}
+
+impl AgentRegistration {
+ pub fn new(
+ new_discovery_handler_sender: broadcast::Sender,
+ registered_discovery_handlers: RegisteredDiscoveryHandlerMap,
+ ) -> Self {
+ AgentRegistration {
+ new_discovery_handler_sender,
+ registered_discovery_handlers,
+ }
+ }
+}
+
+#[tonic::async_trait]
+impl Registration for AgentRegistration {
+ /// Adds new `DiscoveryHandler`s to the RegisteredDiscoveryHandlerMap and broadcasts a message to any running
+ /// DiscoveryOperators that a new `DiscoveryHandler` exists. If the discovery handler is already registered at an
+ /// endpoint and the register request has changed, the previously registered DH is told to stop discovery and is
+ /// removed from the map. Then, the updated DH is registered.
+ async fn register_discovery_handler(
+ &self,
+ request: Request,
+ ) -> Result, Status> {
+ let req = request.into_inner();
+ let dh_name = req.name.clone();
+ let endpoint = req.endpoint.clone();
+ let dh_endpoint = create_discovery_handler_endpoint(
+ &endpoint,
+ EndpointType::from_i32(req.endpoint_type).unwrap(),
+ );
+ info!(
+ "register_discovery_handler - called with register request {:?}",
+ req
+ );
+ let (close_discovery_handler_connection, _) =
+ broadcast::channel(CLOSE_DISCOVERY_HANDLER_CONNECTION_CHANNEL_CAPACITY);
+ let discovery_handler_details = DiscoveryDetails {
+ name: dh_name.clone(),
+ endpoint: dh_endpoint.clone(),
+ shared: req.shared,
+ close_discovery_handler_connection,
+ connectivity_status: DiscoveryHandlerStatus::Waiting,
+ };
+ let mut registered_discovery_handlers = self.registered_discovery_handlers.lock().unwrap();
+ // Check if any DiscoveryHandlers have been registered under this name
+ if let Some(register_request_map) = registered_discovery_handlers.get_mut(&dh_name) {
+ if let Some(dh_details) = register_request_map.get(&dh_endpoint) {
+ // Check if DH at that endpoint is already registered but changed request
+ if dh_details.shared != req.shared || dh_details.endpoint != dh_endpoint {
+ // Stop current discovery with this DH if any. A receiver may not exist if
+ // 1) no configuration has been applied that uses this DH or
+ // 2) a connection cannot be made with the DH's endpoint
+ dh_details
+ .close_discovery_handler_connection
+ .send(())
+ .unwrap_or_default();
+ } else {
+ // Already registered. Return early.
+ return Ok(Response::new(Empty {}));
+ }
+ }
+ // New or updated Discovery Handler
+ register_request_map.insert(dh_endpoint, discovery_handler_details);
+ } else {
+ // First Discovery Handler registered under this name
+ let mut register_request_map = HashMap::new();
+ register_request_map.insert(dh_endpoint, discovery_handler_details);
+ registered_discovery_handlers.insert(dh_name.clone(), register_request_map);
+ }
+ // Notify of new Discovery Handler
+ if self
+ .new_discovery_handler_sender
+ .send(dh_name.clone())
+ .is_err()
+ {
+ // If no configurations have been applied, no receivers can nor need to be updated about the new discovery
+ // handler
+ trace!("register_discovery_handler - new {} discovery handler registered but no active discovery operators to receive the message", dh_name);
+ }
+ Ok(Response::new(Empty {}))
+ }
+}
+
+/// Serves the Agent registration service over UDS.
+pub async fn run_registration_server(
+ discovery_handler_map: RegisteredDiscoveryHandlerMap,
+ new_discovery_handler_sender: broadcast::Sender,
+) -> Result<(), Box> {
+ internal_run_registration_server(
+ discovery_handler_map,
+ new_discovery_handler_sender,
+ &akri_discovery_utils::get_registration_socket(),
+ )
+ .await
+}
+
+pub async fn internal_run_registration_server(
+ discovery_handler_map: RegisteredDiscoveryHandlerMap,
+ new_discovery_handler_sender: broadcast::Sender,
+ socket_path: &str,
+) -> Result<(), Box> {
+ info!("internal_run_registration_server - entered");
+ let registration = AgentRegistration::new(new_discovery_handler_sender, discovery_handler_map);
+ trace!(
+ "internal_run_registration_server - registration server listening on socket {}",
+ socket_path
+ );
+ // Delete socket in case previously created/used
+ std::fs::remove_file(&socket_path).unwrap_or(());
+ let mut uds =
+ tokio::net::UnixListener::bind(socket_path).expect("Failed to bind to socket path");
+ Server::builder()
+ .add_service(RegistrationServer::new(registration))
+ .serve_with_incoming(uds.incoming().map_ok(unix_stream::UnixStream))
+ .await?;
+ trace!(
+ "internal_run_registration_server - gracefully shutdown ... deleting socket {}",
+ socket_path
+ );
+ std::fs::remove_file(socket_path).unwrap_or(());
+ Ok(())
+}
+
+#[cfg(any(test, feature = "agent-full"))]
+pub fn register_embedded_discovery_handlers(
+ discovery_handler_map: RegisteredDiscoveryHandlerMap,
+) -> Result<(), Box> {
+ info!("register_embedded_discovery_handlers - entered");
+ let env_var_query = ActualEnvVarQuery {};
+ inner_register_embedded_discovery_handlers(discovery_handler_map, &env_var_query)?;
+ Ok(())
+}
+
+/// Adds all embedded Discovery Handlers to the RegisteredDiscoveryHandlerMap, specifying an endpoint of
+/// Endpoint::Embedded to signal that it is an embedded Discovery Handler.
+#[cfg(any(test, feature = "agent-full"))]
+pub fn inner_register_embedded_discovery_handlers(
+ discovery_handler_map: RegisteredDiscoveryHandlerMap,
+ query: &impl EnvVarQuery,
+) -> Result<(), Box> {
+ type Details = (String, bool);
+ let mut embedded_discovery_handlers: Vec = Vec::new();
+ if query.get_env_var(ENABLE_DEBUG_ECHO_LABEL).is_ok() {
+ let shared: bool = query
+ .get_env_var(akri_debug_echo::DEBUG_ECHO_INSTANCES_SHARED_LABEL)
+ .unwrap()
+ .parse()
+ .unwrap();
+ embedded_discovery_handlers
+ .push((akri_debug_echo::DISCOVERY_HANDLER_NAME.to_string(), shared));
+ }
+ #[cfg(feature = "onvif-feat")]
+ embedded_discovery_handlers.push((
+ akri_onvif::DISCOVERY_HANDLER_NAME.to_string(),
+ akri_onvif::SHARED,
+ ));
+ #[cfg(feature = "udev-feat")]
+ embedded_discovery_handlers.push((
+ akri_udev::DISCOVERY_HANDLER_NAME.to_string(),
+ akri_udev::SHARED,
+ ));
+ #[cfg(feature = "opcua-feat")]
+ embedded_discovery_handlers.push((
+ akri_opcua::DISCOVERY_HANDLER_NAME.to_string(),
+ akri_opcua::SHARED,
+ ));
+
+ embedded_discovery_handlers.into_iter().for_each(|dh| {
+ let (name, shared) = dh;
+ let (close_discovery_handler_connection, _) =
+ broadcast::channel(CLOSE_DISCOVERY_HANDLER_CONNECTION_CHANNEL_CAPACITY);
+ let discovery_handler_details = DiscoveryDetails {
+ name: name.clone(),
+ endpoint: DiscoveryHandlerEndpoint::Embedded,
+ shared,
+ close_discovery_handler_connection,
+ connectivity_status: DiscoveryHandlerStatus::Waiting,
+ };
+ let mut register_request_map = HashMap::new();
+ register_request_map.insert(
+ DiscoveryHandlerEndpoint::Embedded,
+ discovery_handler_details,
+ );
+ discovery_handler_map
+ .lock()
+ .unwrap()
+ .insert(name, register_request_map);
+ });
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use akri_discovery_utils::discovery::v0::registration_client::RegistrationClient;
+ use akri_shared::os::env_var::MockEnvVarQuery;
+ use std::convert::TryFrom;
+ use tempfile::Builder;
+ use tokio::net::UnixStream;
+ use tonic::transport::{Endpoint, Uri};
+
+ #[test]
+ fn test_register_embedded_discovery_handlers() {
+ let mut seq = mockall::Sequence::new();
+ // Enable debug echo and set environment variable to set whether debug echo instances are shared
+ let mut mock_env_var = MockEnvVarQuery::new();
+ mock_env_var
+ .expect_get_env_var()
+ .times(1)
+ .withf(|label: &str| label == ENABLE_DEBUG_ECHO_LABEL)
+ .in_sequence(&mut seq)
+ .returning(|_| Ok("1".to_string()));
+ mock_env_var
+ .expect_get_env_var()
+ .times(1)
+ .withf(|label: &str| label == akri_debug_echo::DEBUG_ECHO_INSTANCES_SHARED_LABEL)
+ .in_sequence(&mut seq)
+ .returning(|_| Ok("false".to_string()));
+ let discovery_handler_map = Arc::new(Mutex::new(HashMap::new()));
+ inner_register_embedded_discovery_handlers(discovery_handler_map.clone(), &mock_env_var)
+ .unwrap();
+ assert!(discovery_handler_map
+ .lock()
+ .unwrap()
+ .get("debugEcho")
+ .is_some());
+ #[cfg(feature = "onvif-feat")]
+ assert!(discovery_handler_map.lock().unwrap().get("onvif").is_some());
+ #[cfg(feature = "opcua-feat")]
+ assert!(discovery_handler_map.lock().unwrap().get("opcua").is_some());
+ #[cfg(feature = "udev-feat")]
+ assert!(discovery_handler_map.lock().unwrap().get("udev").is_some());
+ }
+
+ #[test]
+ fn test_register_embedded_discovery_handlers_no_debug_echo() {
+ let mut mock_env_var = MockEnvVarQuery::new();
+ mock_env_var
+ .expect_get_env_var()
+ .times(1)
+ .withf(|label: &str| label == ENABLE_DEBUG_ECHO_LABEL)
+ .returning(|_| Err(std::env::VarError::NotPresent));
+ let discovery_handler_map = Arc::new(Mutex::new(HashMap::new()));
+ inner_register_embedded_discovery_handlers(discovery_handler_map.clone(), &mock_env_var)
+ .unwrap();
+ assert!(discovery_handler_map
+ .lock()
+ .unwrap()
+ .get("debugEcho")
+ .is_none());
+ }
+
+ #[tokio::test]
+ async fn test_run_registration_server_reregister_discovery_handler() {
+ let registration_socket_dir = Builder::new().tempdir().unwrap();
+ let registration_socket_path = registration_socket_dir
+ .path()
+ .join("agent-registration.sock");
+ let registration_socket_path_string_thread =
+ registration_socket_path.to_str().unwrap().to_string();
+ let registration_socket_path_string =
+ registration_socket_path.to_str().unwrap().to_string();
+ let (new_discovery_handler_sender, mut new_discovery_handler_receiver) =
+ broadcast::channel(4);
+ let discovery_handler_map = Arc::new(Mutex::new(HashMap::new()));
+ let thread_discovery_handler_map = discovery_handler_map.clone();
+
+ // Run registration service
+ tokio::spawn(async move {
+ internal_run_registration_server(
+ thread_discovery_handler_map,
+ new_discovery_handler_sender,
+ ®istration_socket_path_string_thread,
+ )
+ .await
+ .unwrap();
+ });
+
+ // Make sure registration service is running
+ assert!(unix_stream::try_connect(®istration_socket_path_string)
+ .await
+ .is_ok());
+ // Connect to registration service
+ let channel = Endpoint::try_from("dummy://[::]:50051")
+ .unwrap()
+ .connect_with_connector(tower::service_fn(move |_: Uri| {
+ UnixStream::connect(registration_socket_path_string.clone())
+ }))
+ .await
+ .unwrap();
+ // Create registration client
+ let mut registration_client = RegistrationClient::new(channel);
+
+ // Test registering a discovery handler with UDS endpoint
+ let endpoint_string = "/path/to/socket/name.sock".to_string();
+ let discovery_handler_endpoint = DiscoveryHandlerEndpoint::Uds(endpoint_string.clone());
+ let request = RegisterDiscoveryHandlerRequest {
+ name: "name".to_string(),
+ endpoint: endpoint_string.clone(),
+ endpoint_type: EndpointType::Uds as i32,
+ shared: true,
+ };
+ assert!(registration_client
+ .register_discovery_handler(request.clone())
+ .await
+ .is_ok());
+ assert_eq!(new_discovery_handler_receiver.recv().await.unwrap(), "name");
+ let discovery_handler_details = discovery_handler_map
+ .lock()
+ .unwrap()
+ .get("name")
+ .unwrap()
+ .get(&discovery_handler_endpoint)
+ .unwrap()
+ .clone();
+ assert_eq!(
+ discovery_handler_details.endpoint,
+ DiscoveryHandlerEndpoint::Uds(request.endpoint.clone())
+ );
+ assert_eq!(discovery_handler_details.shared, request.shared);
+
+ // When a discovery handler is re-registered with the same register request, no message should be sent to
+ // terminate any existing discovery clients.
+ let mut stop_discovery_receiver = discovery_handler_details
+ .close_discovery_handler_connection
+ .subscribe();
+ assert!(registration_client
+ .register_discovery_handler(request)
+ .await
+ .is_ok());
+ assert!(stop_discovery_receiver.try_recv().is_err());
+
+ // When a discovery handler at a specified endpoint re-registers at the same endpoint but with a different
+ // locality current discovery handler clients should be notified to terminate and the entry in the
+ // RegisteredDiscoveryHandlersMap should be replaced.
+ let local_request = RegisterDiscoveryHandlerRequest {
+ name: "name".to_string(),
+ endpoint: endpoint_string,
+ endpoint_type: EndpointType::Uds as i32,
+ shared: false,
+ };
+ assert!(registration_client
+ .register_discovery_handler(local_request.clone())
+ .await
+ .is_ok());
+ assert!(stop_discovery_receiver.try_recv().is_ok());
+ let discovery_handler_details = discovery_handler_map
+ .lock()
+ .unwrap()
+ .get("name")
+ .unwrap()
+ .get(&discovery_handler_endpoint)
+ .unwrap()
+ .clone();
+ assert_eq!(
+ discovery_handler_details.endpoint,
+ DiscoveryHandlerEndpoint::Uds(local_request.endpoint)
+ );
+ assert_eq!(discovery_handler_details.shared, local_request.shared);
+ }
+
+ #[test]
+ fn test_create_discovery_handler_endpoint() {
+ // Assert the endpoint with EndpointType::Uds in converted to DiscoveryHandlerEndpoint::Uds(endpoint)
+ assert_eq!(
+ create_discovery_handler_endpoint("/path/to/socket.sock", EndpointType::Uds),
+ DiscoveryHandlerEndpoint::Uds("/path/to/socket.sock".to_string())
+ );
+
+ // Assert the endpoint with EndpointType::Network in converted to DiscoveryHandlerEndpoint::Network(endpoint)
+ assert_eq!(
+ create_discovery_handler_endpoint("http://10.1.2.3:1000", EndpointType::Network),
+ DiscoveryHandlerEndpoint::Network("http://10.1.2.3:1000".to_string())
+ );
+ }
+}
diff --git a/agent/src/util/slot_reconciliation.rs b/agent/src/util/slot_reconciliation.rs
index 9a5f76f2b..c59600a16 100644
--- a/agent/src/util/slot_reconciliation.rs
+++ b/agent/src/util/slot_reconciliation.rs
@@ -2,7 +2,8 @@ use super::{constants::SLOT_RECONCILIATION_CHECK_DELAY_SECS, crictl_containers};
use akri_shared::{akri::instance::Instance, k8s::KubeInterface};
use async_trait::async_trait;
use k8s_openapi::api::core::v1::PodStatus;
-use mockall::predicate::*;
+#[cfg(test)]
+use mockall::{automock, predicate::*};
use std::{
collections::{HashMap, HashSet},
sync::{Arc, Mutex},
@@ -12,15 +13,7 @@ use tokio::process::Command;
type SlotQueryResult = Result, Box>;
-//
-// mockall and async_trait do not work effortlessly together ... to enable both,
-// follow the example here:
-// https://github.com/mibes/mockall-async/blob/53aec15219a720ef5ac483959ff8821cb7d656ae/src/main.rs
-//
-// When async traits are supported by Rust without the async_trait crate, we should
-// add:
-// #[automock]
-//
+#[cfg_attr(test, automock)]
#[async_trait]
pub trait SlotQuery {
async fn get_node_slots(&self) -> SlotQueryResult;
@@ -258,8 +251,7 @@ impl DevicePluginSlotReconciler {
.collect::>();
let modified_instance = Instance {
configuration_name: instance.spec.configuration_name.clone(),
- metadata: instance.spec.metadata.clone(),
- rbac: instance.spec.rbac.clone(),
+ broker_properties: instance.spec.broker_properties.clone(),
shared: instance.spec.shared,
device_usage: modified_device_usage,
nodes: instance.spec.nodes.clone(),
@@ -350,49 +342,14 @@ pub async fn periodic_slot_reconciliation(
}
}
-pub mod test_crictl {
- use super::{SlotQuery, SlotQueryResult};
- use async_trait::async_trait;
- use mockall::predicate::*;
- use mockall::*;
-
- //
- // mockall and async_trait do not work effortlessly together ... to enable both,
- // follow the example here:
- // https://github.com/mibes/mockall-async/blob/53aec15219a720ef5ac483959ff8821cb7d656ae/src/main.rs
- //
- // We can probably eliminate this when async traits are supported by Rust without
- // the async_trait crate.
- //
- mock! {
- pub SlotQueryImpl {
- fn get_node_slots(&self) -> SlotQueryResult;
- }
- }
-
- #[async_trait]
- impl SlotQuery for MockSlotQueryImpl {
- async fn get_node_slots(&self) -> SlotQueryResult {
- self.get_node_slots()
- }
- }
-}
-
#[cfg(test)]
mod reconcile_tests {
- use super::test_crictl::MockSlotQueryImpl;
use super::*;
- use akri_shared::{
- akri::instance::KubeAkriInstanceList, k8s::test_kube::MockKubeImpl, os::file,
- };
+ use akri_shared::{akri::instance::KubeAkriInstanceList, k8s::MockKubeInterface, os::file};
use k8s_openapi::api::core::v1::{PodSpec, PodStatus};
use kube::api::{Object, ObjectList};
- fn configure_get_node_slots(
- mock: &mut MockSlotQueryImpl,
- result: HashSet,
- error: bool,
- ) {
+ fn configure_get_node_slots(mock: &mut MockSlotQuery, result: HashSet, error: bool) {
mock.expect_get_node_slots().times(1).returning(move || {
if !error {
Ok(result.clone())
@@ -402,7 +359,7 @@ mod reconcile_tests {
});
}
- fn configure_get_instances(mock: &mut MockKubeImpl, result_file: &'static str) {
+ fn configure_get_instances(mock: &mut MockKubeInterface, result_file: &'static str) {
mock.expect_get_instances().times(1).returning(move || {
let instance_list_json = file::read_file_to_string(result_file);
let instance_list: KubeAkriInstanceList =
@@ -412,7 +369,7 @@ mod reconcile_tests {
}
fn configure_find_pods_with_field(
- mock: &mut MockKubeImpl,
+ mock: &mut MockKubeInterface,
selector: &'static str,
result_file: &'static str,
) {
@@ -444,7 +401,7 @@ mod reconcile_tests {
grace_period: Duration,
reconciler: &DevicePluginSlotReconciler,
) {
- let mut slot_query = MockSlotQueryImpl::new();
+ let mut slot_query = MockSlotQuery::new();
// slot_query to identify one slot used by this node
configure_get_node_slots(
&mut slot_query,
@@ -452,7 +409,7 @@ mod reconcile_tests {
node_slots.node_slots_error,
);
- let mut kube_interface = MockKubeImpl::new();
+ let mut kube_interface = MockKubeInterface::new();
if !node_slots.node_slots_error {
// kube_interface to find Instance with node-a using slots:
// config-a-359973-1 & config-a-359973-3
diff --git a/agent/src/util/streaming_extension.rs b/agent/src/util/streaming_extension.rs
new file mode 100644
index 000000000..8bc15fce5
--- /dev/null
+++ b/agent/src/util/streaming_extension.rs
@@ -0,0 +1,31 @@
+use akri_discovery_utils::discovery::v0::DiscoverResponse;
+use async_trait::async_trait;
+use tokio::sync::mpsc;
+use tonic::{Code, Status};
+
+/// An extension trait that is used to get the latest message from both embedded and
+/// external Discovery Handlers' streams.
+#[async_trait]
+pub trait StreamingExt: Send {
+ async fn get_message(&mut self) -> Result