Fix the display problem of 2023.2 Specs

This patch adds processing equivalent to the promote-openstack-specs
job to the tacker-spec's Zuul job and fixes the display problem of
2023.2 Specs[1].

Also, change the sphinx extension "sphinxcontrib.plantuml" to
"sphinxcontrib.mermaid" to eliminate the dependency on Java
in the build environment.

[1] https://specs.openstack.org/openstack/tacker-specs/

Closes-Bug: #2037035
Change-Id: I82a1e549b51669e82d10daaad4378f7a049cf95a
This commit is contained in:
Ai Hamano 2023-09-22 15:03:57 +09:00
parent 3203ce9629
commit da42f39961
28 changed files with 407 additions and 397 deletions

View File

@ -1,14 +1,9 @@
- project:
templates:
- openstack-specs-jobs
check:
jobs:
- openstack-tacker-tox-docs
- openstack-tox-pep8
gate:
jobs:
- openstack-tacker-tox-docs
- openstack-tox-pep8
- job:
name: openstack-tacker-tox-docs
parent: openstack-tox-docs
pre-run: playbooks/pre.yaml

View File

@ -48,11 +48,11 @@ confidence in the Zuul result), please execute the following command::
$ tox
Note that you need to install Java such as openjdk_ in advance to run ``tox``
as the plantUML which depends on Java is used inside some specs.
After running ``tox``, the documentation will be available for viewing in HTML
format in the ``doc/build/`` directory. Please do not checkin the generated
HTML files as a part of your commit.
.. _openjdk: https://openjdk.org/install/
Note that even if a syntax error occurs in ``mermaid``, it will not be an
error in ``tox``.
When using ``mermaid`` to create diagrams, you need to carefully check the
built html file.

View File

@ -1 +0,0 @@
graphviz

View File

@ -8,4 +8,4 @@ sphinxcontrib-actdiag # BSD
sphinxcontrib-blockdiag # BSD
sphinxcontrib-nwdiag # BSD
sphinxcontrib-seqdiag # BSD
sphinxcontrib-plantuml # BSD
sphinxcontrib-mermaid # BSD

View File

@ -12,7 +12,6 @@
# serve to show the default.
import datetime
import os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
@ -33,7 +32,7 @@ extensions = [
'sphinxcontrib.actdiag',
'sphinxcontrib.seqdiag',
'sphinxcontrib.nwdiag',
'sphinxcontrib.plantuml',
'sphinxcontrib.mermaid',
'openstackdocstheme',
]
@ -235,5 +234,3 @@ openstackdocs_repo_name = 'openstack/tacker-specs'
openstackdocs_auto_name = False
openstackdocs_bug_project = 'tacker'
openstackdocs_bug_tag = 'doc'
plantuml = f'java -jar {os.path.abspath(".")}/../../tools/plantuml.jar'

View File

@ -1,3 +0,0 @@
- hosts: all
roles:
- ensure-java

View File

@ -6,4 +6,6 @@ Tacker 2023.2 Specifications
:glob:
:maxdepth: 1
support-k8s-cr/*
tf-infra-driver/*
*

View File

@ -0,0 +1,21 @@
@startuml
actor User
package manifest
component ManagementCluster {
component "ClusterAPI" as capi
component "KubernetesAPI" as kapi1
}
component Infrastructure {
component WorkloadCluster {
component "KubernetesAPI" as kapi2
}
}
User --> manifest: 2. create
User -> kapi1: 3. apply manifest
kapi1->capi
capi -> WorkloadCluster: 4. create
User -> ManagementCluster: 1. create
@enduml

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@ -0,0 +1,47 @@
@startuml
frame "python-tackerclient" {
component "tacker-client" as client {
}
}
frame "tacker" {
component "tacker-server" {
component "Server" as serv
}
component "tacker-conductor" {
component "Conductor" as cond
component "Vnflcm driver" as vld
component "Kubernetes\ninfra-driver" as infra
}
}
frame "Management Cluster" as mgmt {
node "Control Plane" as k8s_m_m {
node "Cluster API" as capi
}
node "Worker" as k8s_m_w {
node "Cluster" as cluster
}
}
cloud "Hardware Resources" as hw_w {
frame "Workload Cluster" as wkld {
node "Control Plane" as k8s_w_m
node "Worker" as k8s_w_w
node "Worker" as k8s_w_w2
}
}
'# Relationships
client --> serv: 1. Request\n change current VNF package
serv --> cond
cond --> vld
vld --> infra
infra -right-> k8s_m_m: 2. Call Kubernetes\n API
capi --> cluster: 3. Update the resources
cluster --> wkld: 4. Change the resources of worker nodes
k8s_w_m -[hidden]-> k8s_w_w
k8s_w_m -[hidden]-> k8s_w_w2
@enduml

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -0,0 +1,42 @@
@startuml
frame "python-tackerclient" {
component "tacker-client" as client {
package "VNF Package" as vnfpkg {
file "VNFD" as vnfd
file "CNF (Cluster API)\nDefinition" as cnfd
}
file "Instantiate\nparameters" as inst_param
}
}
frame "tacker" {
component "tacker-server" {
component "Server" as serv
}
component "tacker-conductor" {
component "Conductor" as cond
component "Vnflcm driver" as vld
component "Kubernetes\ninfra-driver" as infra
}
}
frame "Kubernetes Cluster" as k8s {
node "Control Plane" as k8s_m {
node "Cluster API" as capi
}
node "Worker" as k8s_w
}
'# Relationships
vnfpkg --> serv: 1. Request\n create VNF
inst_param --> serv: 2. Request\n instantiate VNF
serv --> cond
cond --> vld
vld --> infra
infra -right-> k8s_m: 3. Call Kubernetes\n API
k8s_m -> capi: 4. Create a CRs\n for Cluster API
capi -[hidden]-> k8s_w
@enduml

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1,63 @@
@startuml
component "Web Server" as w
frame "python-tackerclient" {
component "tacker-client" as client {
package "VNF Package" as vnfpkg {
file "VNFD" as vnfd
file "CNF (k8s Cluster)\nDefinition" as cnfd
file "Scripts for\n Management Driver\n(Credentials Sender)" as mgmtd
}
file "Instantiate\nparameters" as inst_param
}
}
vnfd -[hidden]> cnfd
cnfd -[hidden]> mgmtd
frame "tacker" {
component "tacker-server" {
component "Server" as serv
}
component "tacker-conductor" {
component "Conductor" as cond
component "Vnflcm driver" as vld
component "Kubernetes\ninfra-driver" as infra
}
}
frame "Management Cluster" as mgmt {
node "Control Plane" as k8s_m_m {
node "Cluster API" as capi
}
node "Worker" as k8s_m_w {
node "Cluster" as cluster
}
}
component "Management Driver\n(Credentials Sender)" as mgmtdi
cloud "Hardware Resources" as hw_w {
frame "Workload Cluster" as wkld {
node "Control Plane" as k8s_w_m
node "Worker" as k8s_w_w {
}
}
}
'# Relationships
vnfpkg --> serv: 1. Request\n create VNF
inst_param --> serv: 2. Request\n instantiate VNF
serv --> cond
cond --> vld
vld --> infra
infra -right-> k8s_m_m: 3. Call Kubernetes\n API
capi --> cluster: 4. Create a Cluster Resource
cluster --> wkld: 5. Create a Workload Cluster
k8s_w_m -[hidden]-> k8s_w_w
vld -right-> mgmtdi: 6. Execute management driver
mgmtdi <--- mgmt: 7. Get credentials for Workload Cluster
mgmtdi -> w: 8. Send credentials
@enduml

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -0,0 +1,47 @@
@startuml
frame "python-tackerclient" {
component "tacker-client" as client {
}
}
frame "tacker" {
component "tacker-server" {
component "Server" as serv
}
component "tacker-conductor" {
component "Conductor" as cond
component "Vnflcm driver" as vld
component "Kubernetes\ninfra-driver" as infra
}
}
frame "Management Cluster" as mgmt {
node "Control Plane" as k8s_m_m {
node "Cluster API" as capi
}
node "Worker" as k8s_m_w {
node "Cluster" as cluster
}
}
cloud "Hardware Resources" as hw_w {
frame "Workload Cluster" as wkld {
node "Control Plane" as k8s_w_m
node "Worker" as k8s_w_w
node "Worker" as k8s_w_w2
}
}
'# Relationships
client --> serv: 1. Request\n scale VNF
serv --> cond
cond --> vld
vld --> infra
infra -right-> k8s_m_m: 2. Call Kubernetes\n API
capi --> cluster: 3. Change a parameter\n for the number of worker nodes
cluster --> wkld: 4. Change the number of worker nodes
k8s_w_m -[hidden]-> k8s_w_w
k8s_w_m -[hidden]-> k8s_w_w2
@enduml

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -109,31 +109,7 @@ The following are the characteristics of CAPI:
This figure shows the overview of the operation of CAPI.
.. uml::
@startuml
actor User
package manifest
component ManagementCluster {
component "ClusterAPI" as capi
component "KubernetesAPI" as kapi1
}
component Infrastructure {
component WorkloadCluster {
component "KubernetesAPI" as kapi2
}
}
User --> manifest: 2. create
User -> kapi1: 3. apply manifest
kapi1->capi
capi -> WorkloadCluster: 4. create
User -> ManagementCluster: 1. create
@enduml
.. figure:: ./img/capi-op.svg
Officially supported providers (i.e., cloud platforms) [#capi_providers]_ are:
@ -204,51 +180,7 @@ of this specification.
Upon CRs successfully deployed, CAPI is available on Kubernetes VIM (i.e.,
Kubernetes VIM becomes Management Cluster).
.. uml::
@startuml
frame "python-tackerclient" {
component "tacker-client" as client {
package "VNF Package" as vnfpkg {
file "VNFD" as vnfd
file "CNF (Cluster API)\nDefinition" as cnfd
}
file "Instantiate\nparameters" as inst_param
}
}
frame "tacker" {
component "tacker-server" {
component "Server" as serv
}
component "tacker-conductor" {
component "Conductor" as cond
component "Vnflcm driver" as vld
component "Kubernetes\ninfra-driver" as infra
}
}
frame "Kubernetes Cluster" as k8s {
node "Control Plane" as k8s_m {
node "Cluster API" as capi
}
node "Worker" as k8s_w
}
'# Relationships
vnfpkg --> serv: 1. Request\n create VNF
inst_param --> serv: 2. Request\n instantiate VNF
serv --> cond
cond --> vld
vld --> infra
infra -right-> k8s_m: 3. Call Kubernetes\n API
k8s_m -> capi: 4. Create a CRs\n for Cluster API
capi -[hidden]-> k8s_w
@enduml
.. figure:: ./img/insta-for-capi.svg
Creating Workload Cluster
`````````````````````````
@ -285,72 +217,7 @@ same as the Instantiate CNF.
.. note:: In order to use the Workload Cluster as VIM, users have to register
VIM with the credentials sent by the management driver.
.. uml::
@startuml
component "Web Server" as w
frame "python-tackerclient" {
component "tacker-client" as client {
package "VNF Package" as vnfpkg {
file "VNFD" as vnfd
file "CNF (k8s Cluster)\nDefinition" as cnfd
file "Scripts for\n Management Driver\n(Credentials Sender)" as mgmtd
}
file "Instantiate\nparameters" as inst_param
}
}
vnfd -[hidden]> cnfd
cnfd -[hidden]> mgmtd
frame "tacker" {
component "tacker-server" {
component "Server" as serv
}
component "tacker-conductor" {
component "Conductor" as cond
component "Vnflcm driver" as vld
component "Kubernetes\ninfra-driver" as infra
}
}
frame "Management Cluster" as mgmt {
node "Control Plane" as k8s_m_m {
node "Cluster API" as capi
}
node "Worker" as k8s_m_w {
node "Cluster" as cluster
}
}
component "Management Driver\n(Credentials Sender)" as mgmtdi
cloud "Hardware Resources" as hw_w {
frame "Workload Cluster" as wkld {
node "Control Plane" as k8s_w_m
node "Worker" as k8s_w_w {
}
}
}
'# Relationships
vnfpkg --> serv: 1. Request\n create VNF
inst_param --> serv: 2. Request\n instantiate VNF
serv --> cond
cond --> vld
vld --> infra
infra -right-> k8s_m_m: 3. Call Kubernetes\n API
capi --> cluster: 4. Create a Cluster Resource
cluster --> wkld: 5. Create a Workload Cluster
k8s_w_m -[hidden]-> k8s_w_w
vld -right-> mgmtdi: 6. Execute management driver
mgmtdi <--- mgmt: 7. Get credentials for Workload Cluster
mgmtdi -> w: 8. Send credentials
@enduml
.. figure:: ./img/insta-for-wc.svg
Scale Workload Cluster
``````````````````````
@ -370,56 +237,7 @@ infra-driver supporting CRs of CAPO.
#. Change the number of worker nodes
CAPI changes the number of worker nodes according to the Cluster resource.
.. uml::
@startuml
frame "python-tackerclient" {
component "tacker-client" as client {
}
}
frame "tacker" {
component "tacker-server" {
component "Server" as serv
}
component "tacker-conductor" {
component "Conductor" as cond
component "Vnflcm driver" as vld
component "Kubernetes\ninfra-driver" as infra
}
}
frame "Management Cluster" as mgmt {
node "Control Plane" as k8s_m_m {
node "Cluster API" as capi
}
node "Worker" as k8s_m_w {
node "Cluster" as cluster
}
}
cloud "Hardware Resources" as hw_w {
frame "Workload Cluster" as wkld {
node "Control Plane" as k8s_w_m
node "Worker" as k8s_w_w
node "Worker" as k8s_w_w2
}
}
'# Relationships
client --> serv: 1. Request\n scale VNF
serv --> cond
cond --> vld
vld --> infra
infra -right-> k8s_m_m: 2. Call Kubernetes\n API
capi --> cluster: 3. Change a parameter\n for the number of worker nodes
cluster --> wkld: 4. Change the number of worker nodes
k8s_w_m -[hidden]-> k8s_w_w
k8s_w_m -[hidden]-> k8s_w_w2
@enduml
.. figure:: ./img/scale-for-wc.svg
Update Workload Cluster
```````````````````````
@ -440,55 +258,7 @@ package in Tacker.
#. Change the number of worker nodes
CAPI changes worker nodes according to the Cluster resource.
.. uml::
@startuml
frame "python-tackerclient" {
component "tacker-client" as client {
}
}
frame "tacker" {
component "tacker-server" {
component "Server" as serv
}
component "tacker-conductor" {
component "Conductor" as cond
component "Vnflcm driver" as vld
component "Kubernetes\ninfra-driver" as infra
}
}
frame "Management Cluster" as mgmt {
node "Control Plane" as k8s_m_m {
node "Cluster API" as capi
}
node "Worker" as k8s_m_w {
node "Cluster" as cluster
}
}
cloud "Hardware Resources" as hw_w {
frame "Workload Cluster" as wkld {
node "Control Plane" as k8s_w_m
node "Worker" as k8s_w_w
node "Worker" as k8s_w_w2
}
}
'# Relationships
client --> serv: 1. Request\n change current VNF package
serv --> cond
cond --> vld
vld --> infra
infra -right-> k8s_m_m: 2. Call Kubernetes\n API
capi --> cluster: 3. Update the resources
cluster --> wkld: 4. Change the resources of worker nodes
k8s_w_m -[hidden]-> k8s_w_w
k8s_w_m -[hidden]-> k8s_w_w2
@enduml
.. figure:: ./img/chgpkg-for-wc.svg
Alternatives
------------

View File

@ -0,0 +1,43 @@
@startuml
frame "python-tackerclient" {
component "tacker-client" as client {
package "VNF Package" as vnfpkg {
file "VNFD" as vnfd
file "Terraform\nconfiguration" as tffile
file "Terraform\nvariables\nfile" as tfvar
}
file "Instantiate\nparameters" as inst_param
}
}
vnfd -[hidden]> tffile
tffile-[hidden]> tfvar
frame "tacker" {
component "tacker-server" {
component "server" as serv
}
component "tacker-conductor" {
component "conductor" as cond
component "Terraform\ninfra-driver" as infra
}
}
node "Terraform"
node "Target Service" as ts
cloud "Hardware Resources" as hw {
node "VM" as ins1
}
'# Relationships
vnfpkg --> serv: 1. Request\n create VNF
inst_param --> serv: 2. Request\n instantiate VNF
serv --> cond
cond --> infra
infra --> Terraform: 3. Execute Terraform command
Terraform -right-> ts: 4. Call target\n service API
ts --> ins1: 5. Create VM(s)
@enduml

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,44 @@
@startuml
left to right direction
component "Terraform infra-driver" as tfid
folder "VNF Package A" as pkga
folder "VNF Package B" as pkgb
folder "Directory for VNF Instance A" as da {
file "Configuration" as ca
file "Variables" as va
file "State file A" as statea
file "State lock file A" as statelocka
}
folder "Directory for VNF Instance B" as db {
file "Configuration" as cb
file "Variables" as vb
file "State lock file B" as statelockb
file "State file B" as stateb
}
folder "Directory for VNF Instance C" as dc {
file "Configuration" as cc
file "Variables" as vc
file "State lock file C" as statelockc
file "State file C" as statec
}
dc -[hidden]> db
db -[hidden]> da
component "Terraform" as tf
'# Relationships
tfid -> tf: Execute
tfid <-up- pkga: Download
tfid <-up- pkgb: Download
tfid ---> da: Copy VNF Package A
tfid ---> db: Copy VNF Package A
tfid ---> dc: Copy VNF Package B
tf --> statea: Create
tf --> statelocka: Create
tf --> stateb: Create
tf --> statelockb: Create
tf --> statec: Create
tf --> statelockc: Create
@enduml

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -0,0 +1,22 @@
@startuml
actor "User" as user
component "Terraform CLI" as cli
component "Terraform" as tf
component "Configuration file" as config
component "Variables file" as vars
component "Target Service" as svc
file "State file" as state
file "State lock file" as statelock
'# Relationships
user --> config: 1. Create Configuration file
user --> vars: 1. Create Variables file
user -> cli
cli -> tf: 2. Init Terraform\n with configuration file\n and variables file
tf --> statelock: 3. Create state lock file
cli -> tf: 4. Apply Configuration file
tf -> svc: 5. Call APIs
tf --> state: 6. Create state file
@enduml

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -99,32 +99,7 @@ The following are the characteristics of Terraform:
This figure shows the overview of the operation of Terraform, and input/output
files.
.. uml::
@startuml
actor "User" as user
component "Terraform CLI" as cli
component "Terraform" as tf
component "Configuration file" as config
component "Variables file" as vars
component "Target Service" as svc
file "State file" as state
file "State lock file" as statelock
'# Relationships
user --> config: 1. Create Configuration file
user --> vars: 1. Create Variables file
user -> cli
cli -> tf: 2. Init Terraform\n with configuration file\n and variables file
tf --> statelock: 3. Create state lock file
cli -> tf: 4. Apply Configuration file
tf -> svc: 5. Call APIs
tf --> state: 6. Create state file
@enduml
.. figure:: ./img/tf-op.svg
Terraform Infra-driver
----------------------
@ -149,53 +124,7 @@ Instantiate VNF consist of the following steps:
#. Create VM(s)
Target service (e.g., OpenStack Nova, AWS EC2, etc) creates VM(s).
.. uml::
@startuml
frame "python-tackerclient" {
component "tacker-client" as client {
package "VNF Package" as vnfpkg {
file "VNFD" as vnfd
file "Terraform\nconfiguration" as tffile
file "Terraform\nvariables\nfile" as tfvar
}
file "Instantiate\nparameters" as inst_param
}
}
vnfd -[hidden]> tffile
tffile-[hidden]> tfvar
frame "tacker" {
component "tacker-server" {
component "server" as serv
}
component "tacker-conductor" {
component "conductor" as cond
component "Terraform\ninfra-driver" as infra
}
}
node "Terraform"
node "Target Service" as ts
cloud "Hardware Resources" as hw {
node "VM" as ins1
}
'# Relationships
vnfpkg --> serv: 1. Request\n create VNF
inst_param --> serv: 2. Request\n instantiate VNF
serv --> cond
cond --> infra
infra --> Terraform: 3. Execute Terraform command
Terraform -right-> ts: 4. Call target\n service API
ts --> ins1: 5. Create VM(s)
@enduml
.. figure:: ./img/insta-for-tf.svg
State file management
`````````````````````
@ -229,54 +158,7 @@ model of Tacker or requires another component to manage the state file.
This figure shows the basic idea of the first option.
.. uml::
@startuml
left to right direction
component "Terraform infra-driver" as tfid
folder "VNF Package A" as pkga
folder "VNF Package B" as pkgb
folder "Directory for VNF Instance A" as da {
file "Configuration" as ca
file "Variables" as va
file "State file A" as statea
file "State lock file A" as statelocka
}
folder "Directory for VNF Instance B" as db {
file "Configuration" as cb
file "Variables" as vb
file "State lock file B" as statelockb
file "State file B" as stateb
}
folder "Directory for VNF Instance C" as dc {
file "Configuration" as cc
file "Variables" as vc
file "State lock file C" as statelockc
file "State file C" as statec
}
dc -[hidden]> db
db -[hidden]> da
component "Terraform" as tf
'# Relationships
tfid -> tf: Execute
tfid <-up- pkga: Download
tfid <-up- pkgb: Download
tfid ---> da: Copy VNF Package A
tfid ---> db: Copy VNF Package A
tfid ---> dc: Copy VNF Package B
tf --> statea: Create
tf --> statelocka: Create
tf --> stateb: Create
tf --> statelockb: Create
tf --> statec: Create
tf --> statelockc: Create
@enduml
.. figure:: ./img/op1-local-file.svg
State lock file management
``````````````````````````
@ -365,6 +247,7 @@ Developer impact
infra-driver, such as VNF package format, controllers, conductor, etc, so
that it works in Terraform infra-driver.
Implementation
==============
@ -392,6 +275,7 @@ Dependencies
* Terraform v1.4.0 or later
Testing
=======
@ -405,11 +289,13 @@ other available providers, such as Kubernetes, docker or local provider.
Alternatively, we can use LocalStack [#localstack]_ that acts as a stub of AWS
services.
Documentation Impact
====================
Need to explain the use cases of Terraform infra-driver.
References
==========

View File

@ -30,17 +30,6 @@ Proposed change
Add this file. Please remove later when the another spec is approved.
.. uml::
@startuml
[First component]
[Another component] as Comp2
component Comp3
component [Last\ncomponent] as Comp4
@enduml
Alternatives
------------

View File

@ -36,7 +36,8 @@ Some notes about using this template:
* If you would like to provide a diagram with your spec, text representations
are preferred. http://asciiflow.com/ is a very nice tool to assist with
making ascii diagrams. blockdiag is another tool. These are described below.
making ascii diagrams. blockdiag and mermaid are another tools.
These are described below.
If you require an image (screenshot) for your BP, attaching that to the BP
and checking it in is also accepted. However, text representations are
preferred.
@ -95,7 +96,6 @@ nwdiag
}
}
seqdiag
.. seqdiag::
@ -109,19 +109,57 @@ seqdiag
browser <-- webserver;
}
mermaid Flowchart
* PlantUML examples
.. mermaid::
.. uml::
flowchart LR
A -- text --> B -- text2 --> C
@startuml
mermaid Sequence Diagram
[First component]
[Another component] as Comp2
component Comp3
component [Last\ncomponent] as Comp4
.. mermaid::
@enduml
sequenceDiagram
participant Alice
participant John
Alice->>John: Hello John, how are you?
John-->>Alice: Great!
Alice-)John: See you later!
mermaid State Diagram
.. mermaid::
stateDiagram-v2
state if_state <<choice>>
[*] --> IsPositive
IsPositive --> if_state
if_state --> False: if n < 0
if_state --> True : if n >= 0
mermaid C4 Diagram
.. mermaid::
C4Component
Person(user, "User")
Component(cli, "CLI")
Boundary(services, "Services", ""){
Component(svc1, "Service1")
Component(svc2, "Service2")
}
Boundary(db, "DBs", ""){
ComponentDb(db1, "DB1")
ComponentDb(db2, "DB2")
}
Rel(user, cli, "")
Rel(cli, svc2, "")
Rel(svc2, db1, "")
.. note::
C4 Diagram of mermaid is an experimental diagram for now.
The syntax and properties can change in future releases.
Problem description

Binary file not shown.