mirror of
https://github.com/kerberos-io/documentation.git
synced 2026-08-23 15:18:31 +00:00
add workflows
This commit is contained in:
31
.github/workflows/create-pr.yaml
vendored
Normal file
31
.github/workflows/create-pr.yaml
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
name: Build pull request
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize]
|
||||||
|
jobs:
|
||||||
|
build-pull-request:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
- name: Log in to Docker Hub
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
- uses: actions/setup-node@v1 #this installs node and npm for us
|
||||||
|
with:
|
||||||
|
node-version: "10.x"
|
||||||
|
# Build the Angular application to the dist folder
|
||||||
|
- name: Build Angular application
|
||||||
|
run: |
|
||||||
|
cd kerberos.ng
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
# Build the Docker image with the latest tag and the release tag
|
||||||
|
- name: Build Docker image
|
||||||
|
run: |
|
||||||
|
docker build -t uugai/kerberos-documentation:latest .
|
||||||
53
.github/workflows/create-release.yml
vendored
Normal file
53
.github/workflows/create-release.yml
vendored
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
name: Create a new release
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [created]
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
tag:
|
||||||
|
description: "Tag for the Docker image"
|
||||||
|
required: true
|
||||||
|
default: "test"
|
||||||
|
jobs:
|
||||||
|
build-and-push:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
- name: Log in to Docker Hub
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
# Build the Docker image with the latest tag and the release tag
|
||||||
|
- name: Build and push Docker image with latest tag
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
if: github.event.inputs.tag != 'test'
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
tags: uugai/kerberos-documentation:latest
|
||||||
|
- name: Build and push Docker image with release tag
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
if: github.event.inputs.tag != 'test'
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
tags: uugai/kerberos-documentation:${{ github.event.inputs.tag || github.ref_name }}
|
||||||
|
# After we build the Docker image, we create a pull request to update the GitOps repository
|
||||||
|
# This will allow us to update the Helm chart with the new Docker image tag.
|
||||||
|
- name: Create GitOps Pull Request
|
||||||
|
uses: cedricve/gitops-pullrequest-action@master
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.TOKEN }}
|
||||||
|
gitops-repo: "uug-ai/gitops"
|
||||||
|
gitops-file: "environments/staging/doc.kerberos.io/deployment.yaml"
|
||||||
|
gitops-pr-branch: "release-kerberos-documentation-${{ github.event.inputs.tag || github.ref_name }}"
|
||||||
|
gitops-key: ".spec.template.spec.containers[0].image"
|
||||||
|
gitops-value: "uugai/kerberos-documentation:${{ github.event.inputs.tag || github.ref_name }}"
|
||||||
|
commit-email: "gitops@uug.ai"
|
||||||
|
commit-name: "GitOps - UUG.AI"
|
||||||
|
commit-message: "A new release for Kerberos documentation - ${{ github.event.inputs.tag || github.ref_name }}"
|
||||||
28
.github/workflows/docker.yml
vendored
28
.github/workflows/docker.yml
vendored
@@ -1,28 +0,0 @@
|
|||||||
name: Docker Production build
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-amd64:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
architecture: [amd64]
|
|
||||||
steps:
|
|
||||||
- name: Login to DockerHub
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v2
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v2
|
|
||||||
- name: Available platforms
|
|
||||||
run: echo ${{ steps.buildx.outputs.platforms }}
|
|
||||||
- name: Run Buildx
|
|
||||||
run: docker buildx build --platform linux/${{matrix.architecture}} -t kerberos/documentation:$(echo $GITHUB_SHA | cut -c1-7) --push .
|
|
||||||
37
.github/workflows/gh-pages.yml
vendored
37
.github/workflows/gh-pages.yml
vendored
@@ -1,37 +0,0 @@
|
|||||||
name: github pages
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
- develop
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deploy:
|
|
||||||
runs-on: ubuntu-18.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
submodules: recursive # Fetch Hugo themes (true OR recursive)
|
|
||||||
fetch-depth: 1 # Fetch all history for .GitInfo and .Lastmod
|
|
||||||
|
|
||||||
- name: Setup Hugo
|
|
||||||
uses: peaceiris/actions-hugo@v2
|
|
||||||
with:
|
|
||||||
hugo-version: 'latest'
|
|
||||||
extended: true
|
|
||||||
|
|
||||||
- name: Install npm packages
|
|
||||||
run: |
|
|
||||||
npm install # reads info from package.js
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: hugo --minify --environment production # uses default config/_default/* files + merges with config/production ones
|
|
||||||
|
|
||||||
- name: Deploy
|
|
||||||
uses: peaceiris/actions-gh-pages@v3
|
|
||||||
if: github.ref == 'refs/heads/move-to-hugo-doks'
|
|
||||||
with:
|
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
publish_dir: ./public
|
|
||||||
19
.github/workflows/pr-description.yml
vendored
Normal file
19
.github/workflows/pr-description.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
name: Autofill PR description
|
||||||
|
|
||||||
|
on: pull_request
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
openai-pr-description:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Autofill PR description if empty using OpenAI
|
||||||
|
uses: cedricve/azureopenai-pr-description@master
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.TOKEN }}
|
||||||
|
openai_api_key: ${{ secrets.OPENAI_API_KEY }}
|
||||||
|
azure_openai_api_key: ${{ secrets.AZURE_OPENAI_API_KEY }}
|
||||||
|
azure_openai_endpoint: ${{ secrets.AZURE_OPENAI_ENDPOINT }}
|
||||||
|
azure_openai_version: ${{ secrets.AZURE_OPENAI_VERSION }}
|
||||||
|
overwrite_description: true
|
||||||
5
.idea/.gitignore
generated
vendored
5
.idea/.gitignore
generated
vendored
@@ -1,5 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Editor-based HTTP Client requests
|
|
||||||
/httpRequests/
|
|
||||||
12
.idea/documentation.iml
generated
12
.idea/documentation.iml
generated
@@ -1,12 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="WEB_MODULE" version="4">
|
|
||||||
<component name="NewModuleRootManager">
|
|
||||||
<content url="file://$MODULE_DIR$">
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
|
||||||
</content>
|
|
||||||
<orderEntry type="inheritedJdk" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
</component>
|
|
||||||
</module>
|
|
||||||
4
.idea/encodings.xml
generated
4
.idea/encodings.xml
generated
@@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
|
|
||||||
</project>
|
|
||||||
6
.idea/inspectionProfiles/Project_Default.xml
generated
6
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -1,6 +0,0 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<profile version="1.0">
|
|
||||||
<option name="myName" value="Project Default" />
|
|
||||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
|
||||||
</profile>
|
|
||||||
</component>
|
|
||||||
6
.idea/misc.xml
generated
6
.idea/misc.xml
generated
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="JavaScriptSettings">
|
|
||||||
<option name="languageLevel" value="JSX" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
8
.idea/modules.xml
generated
8
.idea/modules.xml
generated
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/documentation.iml" filepath="$PROJECT_DIR$/.idea/documentation.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
6
.idea/vcs.xml
generated
6
.idea/vcs.xml
generated
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
BIN
.yarn/install-state.gz
Normal file
BIN
.yarn/install-state.gz
Normal file
Binary file not shown.
1
.yarnrc.yml
Normal file
1
.yarnrc.yml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
nodeLinker: node-modules
|
||||||
@@ -1,9 +1,15 @@
|
|||||||
[[docs]]
|
[[docs]]
|
||||||
name = "Prologue"
|
name = "Prologue"
|
||||||
weight = 10
|
weight = 9
|
||||||
identifier = "prologue"
|
identifier = "prologue"
|
||||||
url = "/prologue/"
|
url = "/prologue/"
|
||||||
|
|
||||||
|
[[machinelearning]]
|
||||||
|
name = "Machine learning"
|
||||||
|
weight = 10
|
||||||
|
identifier = "machinelearning"
|
||||||
|
url = "/machinelearning/"
|
||||||
|
|
||||||
[[agent]]
|
[[agent]]
|
||||||
name = "Agent"
|
name = "Agent"
|
||||||
weight = 11
|
weight = 11
|
||||||
|
|||||||
9
content/machinelearning/_index.md
Normal file
9
content/machinelearning/_index.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
title: "Machine learning"
|
||||||
|
description: ""
|
||||||
|
lead: ""
|
||||||
|
date: 2020-10-06T08:49:15+00:00
|
||||||
|
lastmod: 2020-10-06T08:49:15+00:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
---
|
||||||
492
content/machinelearning/first-things-first/gpu-to-cpu.svg
Normal file
492
content/machinelearning/first-things-first/gpu-to-cpu.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 426 KiB |
112
content/machinelearning/first-things-first/index.md
Normal file
112
content/machinelearning/first-things-first/index.md
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
---
|
||||||
|
title: "First things first"
|
||||||
|
description: "Machine learning without a UI, is just a model nobody can use."
|
||||||
|
lead: "Machine learning without a UI, is just a model nobody can use."
|
||||||
|
date: 2020-10-06T08:49:31+00:00
|
||||||
|
lastmod: 2020-10-06T08:49:31+00:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
menu:
|
||||||
|
machinelearning:
|
||||||
|
parent: "machinelearning"
|
||||||
|
weight: 101
|
||||||
|
toc: true
|
||||||
|
---
|
||||||
|
|
||||||
|
Machine learning or computer vision at scale is one of the key motivations of the entire Kerberos.io software stack. All services going from Kerberos Agents, Kerberos Vault to Kerberos Hub are designed in such a way that you can scale them independently of each other.
|
||||||
|
|
||||||
|
The design principle of Kerberos.io allows to decouple videos streams from any CPU or GPU workloads, through the creation of video chunks, queueing, event messaging and/or real-time processing. This is perfect for GPU workloads such as machine learning or other AI services.
|
||||||
|
|
||||||
|
{{< figure src="vault-ml-cv.svg" alt="Machine learning and Computer Vision with Kerberos Vault." caption="Machine learning and Computer Vision with Kerberos Vault" class="stretch">}}
|
||||||
|
|
||||||
|
## The problem we solve
|
||||||
|
|
||||||
|
The complexity not only starts with the creation of a machine learning model, to solve or compute a specific business case, it also starts at the deployment of the machine learning model, and it even becomes more complex when you scale over time.
|
||||||
|
|
||||||
|
Especially in the video analytics space, where we apply machine learning to video streams, and more specific videos (mp4) or images (JPEG), there are many challenges.
|
||||||
|
|
||||||
|
1. How do you scale the recording of video streams?
|
||||||
|
2. How do GPUs process video streams? One by one?
|
||||||
|
3. How do you scale your GPUs, and add more in the future?
|
||||||
|
4. What happens if you add more video streams, do you need to add more GPUs?
|
||||||
|
|
||||||
|
Previously challenges is where we at Kerberos.io bring value through
|
||||||
|
[the Kerberos Enterprise Suite](/enterprise/first-things-first). We provide an elegant and scalable design for supporting your every-growing machine learning use cases. Read more below.
|
||||||
|
|
||||||
|
## Video chunks
|
||||||
|
|
||||||
|
Kerberos Agents are responsible for recording video streams into small chunks, mp4s, of which you can configure the length of the recording; pre and post recording. The main motivation of doing this is that small chunks of videos are easier to process and distribute across different workloads. Therefore, video chunks are the building blocks and basis of the scale Kerberos brings.
|
||||||
|
|
||||||
|
## Queueing and events
|
||||||
|
|
||||||
|
Video chunks prepared by one or more Kerberos Agents are sent to Kerberos Vault where they are persisted in one or more storage providers. The key thing is that they stored through a central application, Kerberos Vault, which has the ability to trigger integrations such as a Kafka broker.
|
||||||
|
|
||||||
|
## Consuming and interfere
|
||||||
|
|
||||||
|
Once events are being generated, they are ready to be consumed by one or more consumers. Having this publish and subscribe mechanism, for example Kafka, you have one or more producers (Kerberos Vault replicas) and one or more consumers (ML clients). This allows one to scale the number of consumer and workloads as the number of events, and thus recordings, are increasing.
|
||||||
|
|
||||||
|
## NVIDIA operator
|
||||||
|
|
||||||
|
Having multiple consumers, I believe we can agree that scaling becomes trivial and thanks to Kubernetes more flexible. But what about GPUs, how do we scale that?
|
||||||
|
|
||||||
|
This is where the NVIDIA operator pops up. NVIDIA released a Kubernetes operator called [the NVIDIA operator](https://developer.nvidia.com/blog/nvidia-gpu-operator-simplifying-gpu-management-in-kubernetes/), that takes control over GPU allocation and assignment to workloads.
|
||||||
|
|
||||||
|
The operator solves two main challenges:
|
||||||
|
|
||||||
|
1. Hardware: a system or infrastructure engineer can just insert another NVIDIA GPU in one of the PCIE slots, and the Kubernetes cluster / NVIDIA operator will make it part of the GPU pool.
|
||||||
|
|
||||||
|
2. Datascience: engineers can build and deploy machine learning models without the need of specifying a particular GPU. The NVIDIA operator will handle that allocation, and make sure one of the GPUs in the pool is assigned to the requested workload.
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
The installation of [the NVIDIA operator can be found here](https://github.com/kerberos-io/nvidia-gpu-kubernetes), with a couple of examples of how to integrate a workload (a machine learning model) with Kerberos Vault.
|
||||||
|
|
||||||
|
{{< figure src="yolov3-integration-kerberos-vault.png" alt="The NVIDIA operator brings scale to your GPUs." caption="The NVIDIA operator brings scale to your GPUs." class="stretch">}}
|
||||||
|
|
||||||
|
### An example
|
||||||
|
|
||||||
|
The goal of the integration feature of Kerberos Vault is to allow an enterprise to bring its custom logic, while relying on a stable and scalable video management system. One can build its own notification service, triggering IoT or other kind of sensors, or execute a custom machine learning model trying to detect specific objects, patterns or actions in a recording. As described below [you can found a complete aswer here](https://github.com/kerberos-io/vault-ml).
|
||||||
|
|
||||||
|
{{< figure src="yolov3-integration-kerberos-vault-example.png" alt="The NVIDIA operator brings scale to your GPUs." caption="The NVIDIA operator brings scale to your GPUs." class="stretch">}}
|
||||||
|
|
||||||
|
So having above example let us get a bit more concrete on how this is functioning. We will describe the different steps and what is happening under the hood.
|
||||||
|
|
||||||
|
- **Step 0**: Small chunks of recordings are created, persisted in a storage provider, and a message is sent to a Kafka topic.
|
||||||
|
|
||||||
|
- **Step 1**: an extension/workload which executes the YOLOv3 model is consuming Kafka messages from a topic, produced by Kerberos Vault.
|
||||||
|
|
||||||
|
- **Step 2**: Before execution of the model, the extension consumes the relevant messages from a specific topic, and downloads the video chunk from Kerberos Vault in memory.
|
||||||
|
|
||||||
|
- **Step 3**: The NVIDIA operator has assigned one of the GPUs from the GPU pool to the extension. The assigned GPU is used to execute the YOLOv3 model on the video chunk.
|
||||||
|
|
||||||
|
- **Step 4**: Metadata is computed, for example number of objects detected, and is injected into a resulting Kafka topic.
|
||||||
|
|
||||||
|
- **Step 5**: An additional (optional) microservice is reading from the resulting Kafka topic, and execute more specific business logic: alerting through sensors or notifications, creation of entries into a support or CCTV system, etc.
|
||||||
|
|
||||||
|
## Scale and expand
|
||||||
|
|
||||||
|
As illustrated in previous example, it should become clear that by chunking videos, distributing them over a flexible pool of GPUs, allows to provide a scalable and flexible way. Video streams are decouples from GPUs, and each of them can be scaled independently. Video chunks created by one or more Kerberos Agents are distributed over a pool of GPUs without knowing upfront which GPU will process which video chunk from which Kerberos Agent.
|
||||||
|
|
||||||
|
{{< figure src="gpu-to-cpu.svg" alt="GPUs and CPUs are decoupled. Any recording, independent of any video stream, will be distributed to the GPU pool." caption="GPUs and CPUs are decoupled. Any recording, independent of any video stream, will be distributed to the GPU pool." class="stretch">}}
|
||||||
|
|
||||||
|
## Machine learning at the edge
|
||||||
|
|
||||||
|
Execution of your machine learning at the edge will give you a lot of advantages and allow you to distribute and scale more easily and efficient. By executing at the edge, you will:
|
||||||
|
|
||||||
|
- Lower cloud storage and compute,
|
||||||
|
- better latency and response times,
|
||||||
|
- real distributed processing and computing power,
|
||||||
|
- ability to tune and provide only the hardware required.
|
||||||
|
|
||||||
|
## Let your data scientist do datascience
|
||||||
|
|
||||||
|
Your data scientist want to do datascience, they don't want to set up infrastructure, install GPUs or even modify their models and programming language to select a specific GPU. Hardware and more specific GPUs should remain transparent.
|
||||||
|
|
||||||
|
The entire design behind the Kerberos Enterprise Suite is to support and implement the concept of MLOps. This means that your data scientist should focus on the data science and the creation of specific models.
|
||||||
|
|
||||||
|
When delivering or releasing a model, a data scientist should only be required to make a build, a container, of his release and deploy that inside a Kubernetes cluster. Nothing more than that.
|
||||||
|
|
||||||
|
A data scientist should make the assumption that once it is deployed:
|
||||||
|
1. it is automatically scaled across the different hardware components (GPUs).
|
||||||
|
2. it just runs and has fail over and high-availability enabled by default
|
||||||
|
3. only works with video chunks and metadata, but not the actual video streams.
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.1 MiB |
559
content/machinelearning/first-things-first/vault-ml-cv.svg
Normal file
559
content/machinelearning/first-things-first/vault-ml-cv.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 461 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 128 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 93 KiB |
@@ -21,6 +21,17 @@
|
|||||||
</ul>
|
</ul>
|
||||||
{{ end -}}
|
{{ end -}}
|
||||||
{{ end -}}
|
{{ end -}}
|
||||||
|
{{ range .Site.Menus.machinelearning -}}
|
||||||
|
<h3>{{ .Name }}</h3>
|
||||||
|
{{ if .HasChildren -}}
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
{{ range .Children -}}
|
||||||
|
{{- $active := or ($currentPage.IsMenuCurrent "agent" .) ($currentPage.HasMenuCurrent "agent" .) -}}
|
||||||
|
<li><a class="docs-link{{ if $active }} active{{ end }}" href="{{ .URL | relURL }}">{{ .Name }}</a></li>
|
||||||
|
{{ end -}}
|
||||||
|
</ul>
|
||||||
|
{{ end -}}
|
||||||
|
{{ end -}}
|
||||||
{{ range .Site.Menus.enterprise -}}
|
{{ range .Site.Menus.enterprise -}}
|
||||||
<h3>{{ .Name }}</h3>
|
<h3>{{ .Name }}</h3>
|
||||||
{{ if .HasChildren -}}
|
{{ if .HasChildren -}}
|
||||||
|
|||||||
Reference in New Issue
Block a user