Closed DarwinJS closed 2 years ago
I've run the example against the last couple of version of PlantUML and C4-PlantUML and I keep getting the above image. I have not been able to reproduce the image from #236.
Was the original image created with a distributed JAR or on the online server?
@Potherca - wow thanks for all the testing!
I found that on https://kroki.io, if you select "C4 with PlantUML" it renders how I was expecting.
wow thanks for all the testing!
No worries, I do a lot of PlantUML development, so I already have a local test setup. I just throw the .puml
file into my setup and the rest goes automagically. Something I hope to add soonish is a visual diff so my test setup can tell me when a change (such as you noted) can be automatically found.
Speaking of which, when i run Kroki with this:
@startuml
version
@enduml
I get:
PlantUML version 1.2022.5(Sat Apr 30 10:55:52 GMT 2022)(GPL source distribution) This version of PlantUML is 136 days old, so you shouldconsider upgrading
Which is odd, since that is not what I see with the distributed 1.2022.5 jar file. I'll have to do some more digging to see where the difference comes from.
Its good to have a reproducible case, that gives me a point to start from. I'll report back here if/when I find something.
I guess the more important detail is that the rendering I am getting directly from plantuml does not follow what is in my file. It blatantly ignores both directional Lay and Rel to put GitLab Runner on the entirely wrong side of the chart.
I have tried reordering the directional directives, reversing them, trying them on top level objects rather than most enclosed, I've tried rotation layouts and changing all the relationships and I've tried moving where Lay and Rel appear in relationship to each other and in relationship to the shape declarations.
It would sure be helpful to have a published list of supposed rendering priorities - both for users and developers to test against.
Something like:
Or perhaps rending priorities mean that you should purposely use directional Rel and Lay statements as soon as you can (as soon as all entities are created).
For instance, some questions: Q1: what is the stronger priority for object relationships - Rel or Lay ? Q2: is it OK or unexpected to use directional versions of both Rel and Lay in the same diagram? Q3: should Rel and Lay commands be used as soon as their target entities exist or is it better to do them altogether after all entities are created or should it not matter at all (i.e. Statement ordering has no effect on layout) ?
The one factor of unpredictable and uncontrollable layout makes the tool quite unusable for my cases.
Hi @DarwinJS,
at Q1: Rel is the logical relationship; Lay is only used for the layout if the elements have nor relationship (without an arrow, practically it is an hidden arrow without a label) at Q2: you can use Rel (default without a predefined layout hint), Rel and Lay in one diagram; If you have already a Rel() and you need a layout direction then it is better to use directly Rel#() instead of the 2 calls Rel() and Lay at Q3: I would start with a) all groups/elements b) add relationships (Rel()) c1) if the main position of an element does not match, then it could be simpler to define the elements in a different order (e.g. I changed the order of the boundaries) c2) use Rel# or Lay_# if the layout does not match
BR Helmut
PS.: With following source PlantUML server and kroki.io created the same output for me
@startuml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Dynamic.puml
title GitLab Runner and Kubernetes Agent Connections
AddRelTag("network", $textColor="red", $lineColor="red", $lineStyle = DashedLine())
AddRelTag("auth", $textColor="orange", $lineColor="orange", $lineStyle = DashedLine())
AddRelTag("control", $textColor="green", $lineColor="green")
Boundary(glinstenv, "Any Environment", "Private,Cloud,etc") {
Boundary(glinstance, "Any GitLab Instance", "SaaS, Self-Managed") {
Component(kas, "KAS", "Spring Bean")
Component(glrails, "GitLab Rails",)
Lay_D(glrails,kas)
}
}
Boundary(k8senv, "Any Environment", "Private,Cloud,etc") {
Boundary(kasnet, "Any K8s Cluster") {
Component(k8sagent, "GL Agent for K8s")
}
}
Boundary(network, "Any Networks", "Private, Internet, etc") {
Boundary(runnerenv, "Any Environment", "Private,Cloud,etc") {
Container(glrunner, "GitLab Runner") {
Component(glrunnertradconn, "Job Acquisition Connection")
Component(glrunneragtconn, "Agent Connection")
}
}
Rel(glrunnertradconn,glrunneragtconn, "K8s Command", "w/ KUBE_CONTEXT")
'Runner Polling Connection to Job Queue
Rel_R(glrunnertradconn, glrails, "Initiates", "SSL", $tags="network")
Rel_R(glrunnertradconn, glrails, "Initiates", "Token Auth", $tags="auth")
Rel_R(glrunnertradconn, glrails, "Control","Poll & Pull Jobs", $tags="control")
'Runner Connection to KAS
Rel_R(glrunneragtconn, kas, "Initiates", "SSL", $tags="network")
Rel_R(glrunneragtconn, kas, "Control","Push K8s CMDs", $tags="control")
Rel_R(glrunneragtconn, kas, "Starts", "Token Auth", $tags="auth")
'K8s Agent to KAS
Rel_R(kas, k8sagent, "Initiates", "SSL", $tags="network")
Rel_R(kas, k8sagent, "Initiates", "Token Auth", $tags="auth")
Rel_R(kas, k8sagent, "Control","Poll & Pull Manifests", $tags="control")
'Layouts prioritized in order - put outer most first
Lay_R(runnerenv,glinstenv)
Lay_R(glinstenv,k8sagent)
'Rel_R(glrunner,kas, "Control", "Push CD", $tags="control")
'Rel_R(kas,k8sagent, "Control", "Push CD", $tags="control")
}
SHOW_LEGEND()
@enduml
PPS: @Potherca my first idea was that kroki uses some additional styles/layouts like skinparam ...
or !layout elk
, but I checked the generated source (which is stored in the svg files too) and found no kroki specific extension
@kirchsth - thanks for your help!
In rebuilding what I have with what you provided I may have discovered a bug.
You accidentally moved Boundary(network, "Any Networks", "Private, Internet, etc") {
to no longer include all other shapes.
When I moved it back to the top - everything stays stable.
HOWEVER, If I move the closing brace from enclosing the Rel and Lay commands and put it right before the first Rel - I get the messed up version.
It seems like if you have an "all enclosing" boundary - it must also enclose all the relationships that occur with in it.
This also includes SHOW_FLOAGING_LEGEND() - if it is not inside the all enclosing boundary.
Working Version:
@startuml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Dynamic.puml
'GitLab Colors
' Light Orange: #FCA326, Medium Orange: #FC6D26, Dark Orange: #E24329
'Layout Rendering Priorities
' 1. With dynamic rendering tools (e.g. VS Code plugin) do NOT trust the first rendering
' as it is shifty when adding stuff - even comments. Wait for a bit or close and reopen preview panel.
' 2. Create all components, containers and boundaries first - in order top to bottom or left to right
' 3. Use Rel (directionless) where possible.
' 3. Use Rel_<direction> to force shape layouts.
' 3.a. Order inner objects first when it creates the desired result.
' 3.b. Try NOT to apply order to both inner elements and elements that enclose them.
' 3.c. Make all orderings at the same nesting level whenever possible.
' 4. Add "Lay_<direction>" to force any layouts that Rel_<direction> does not resolve.
' 5. If you have an "All enclosing" boundary, all of the above Rel and Lay statements must
' be inside the "All enclosing" boundary.
' 6. Legend statements must come after at least one usage of each of the elements you want the legend
' to contain.
title GitLab Runner and Kubernetes Agent Connections
AddRelTag(network, $textColor="red", $lineColor="red", $lineStyle = DashedLine(),$legendText="Network Connection")
AddRelTag(auth, $textColor="orange", $lineColor="orange", $lineStyle = DashedLine(),$legendText="Authentication")
AddRelTag(control, $textColor="green", $lineColor="green",$legendText="App Control Flow")
UpdateElementStyle(boundary, $bgColor="transparent",$fontColor="black", $borderColor="gray",$legendText="Network or Environment")
UpdateElementStyle(component,$bgColor="#FC6D26", $borderColor="transparent",$legendText="GitLab Component")
UpdateElementStyle(container,$bgColor="#FCA326", $borderColor="transparent",$legendText="GitLab Container")
AddBoundaryTag(invisible, $bgColor="transparent",$borderColor="transparent")
Boundary(network, "Any Networks", "Private, Internet, etc") {
Boundary(runnerenv, "Any Environment", "Private,Cloud,etc") {
Container(glrunner, "GitLab Runner") {
Component(glrunnertradconn, "Job Acquisition Connection")
Component(glrunneragtconn, "Agent Connection")
}
}
Boundary(k8senv, "Any Environment", "Private,Cloud,etc") {
Boundary(kasnet, "Any K8s Cluster") {
Component(k8sagent, "GL Agent for K8s")
}
}
Boundary(glinstenv, "Any Environment", "Private,Cloud,etc", $tags="network") {
Container(glinstance, "Any GitLab Instance", "SaaS, Self-Managed") {
Component(kas, "KAS")
Component(glrails, "GitLab Rails",)
}
}
Rel(glrunnertradconn,glrunneragtconn, "K8s Command", "w/ KUBE_CONTEXT")
'Runner Polling Connection to Job Queue
setIndex(1)
Rel_R(glrunnertradconn, glrails, "Initiates", "SSL", $tags="network")
Rel_R(glrunnertradconn, glrails, "Initiates", "Token Auth", $tags="auth")
Rel_R(glrunnertradconn, glrails, "Control","Poll & Pull Jobs", $tags="control")
'Runner Connection to KAS
setIndex(1)
Rel_R(glrunneragtconn, kas, "Initiates", "SSL", $tags="network")
Rel_R(glrunneragtconn, kas, "Starts", "Token Auth", $tags="auth")
Rel_R(glrunneragtconn, kas, "Control","Push CD K8s CMDs", $tags="control")
'K8s Agent to KAS
setIndex(1)
Rel_R(kas, k8sagent, "Initiates", "SSL", $tags="network")
Rel_R(kas, k8sagent, "Initiates", "Token Auth", $tags="auth")
Rel_R(kas, k8sagent, "Control","Poll & Pull Manifests", $tags="control")
Rel_R(kas, k8sagent, "Control","Push CD K8s CMDs", $tags="control")
'Layouts prioritized in order - put outer most first
Lay_D(glrails,kas)
Lay_R(runnerenv,glinstenv)
Lay_R(glinstenv,k8sagent)
' Boundary(legend3, "Legend", $tags="invisible") {
' note as legend4
' <#white,#white>|<color:$LEGEND_TITLE_COLOR>Legend</color> |
' $showActiveLegendEntries($tagDefaultLegend)
' $showActiveLegendEntries($tagCustomLegend)
' endnote
' }
' Lay_R(glinstenv,legend3)
'Closing brace for all enclosing "Any Networks" must stay here
}
'SHOW_LEGEND()
center footer \n\nEach Network Environment must provided suitable network routing, DNS and firewall configuration to allow\nthe connection initiation and return conversations indicated in the lines marked with "[Network]" boxes.\n\nThe GitLab Instance, Runners and Kubernetes Agents may all be in the same network environment - the **most complex case** is depicted because \nit would be the norm if using GitLab.com SaaS with self-hosted runners to manage Kubernetes clusters in a variety of other environments.\n\nAll dark orange boxes are part of a GitLab Instance.\n\nOnly the GitLab K8s integration is required for a pure GitOps workflow (depicted as "(G)").
@enduml
@startuml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Dynamic.puml
'GitLab Colors
' Light Orange: #FCA326, Medium Orange: #FC6D26, Dark Orange: #E24329
'Layout Rendering Priorities
' 1. With dynamic rendering tools (e.g. VS Code plugin) do NOT trust the first rendering
' as it is shifty when adding stuff - even comments. Wait for a bit or close and reopen preview panel.
' 2. Create all components, containers and boundaries first - in order top to bottom or left to right
' 3. Use Rel (directionless) where possible.
' 3. Use Rel_<direction> to force shape layouts.
' 3.a. Order inner objects first when it creates the desired result.
' 3.b. Try NOT to apply order to both inner elements and elements that enclose them.
' 3.c. Make all orderings at the same nesting level whenever possible.
' 4. Add "Lay_<direction>" to force any layouts that Rel_<direction> does not resolve.
' 5. If you have an "All enclosing" boundary, all of the above Rel and Lay statements must
' be inside the "All enclosing" boundary.
' 6. Legend statements must come after at least one usage of each of the elements you want the legend
' to contain.
title GitLab Runner and Kubernetes Agent Connections
AddRelTag(network, $textColor="red", $lineColor="red", $lineStyle = DashedLine(),$legendText="Network Connection")
AddRelTag(auth, $textColor="orange", $lineColor="orange", $lineStyle = DashedLine(),$legendText="Authentication")
AddRelTag(control, $textColor="green", $lineColor="green",$legendText="App Control Flow")
UpdateElementStyle(boundary, $bgColor="transparent",$fontColor="black", $borderColor="gray",$legendText="Network or Environment")
UpdateElementStyle(component,$bgColor="#FC6D26", $borderColor="transparent",$legendText="GitLab Component")
UpdateElementStyle(container,$bgColor="#FCA326", $borderColor="transparent",$legendText="GitLab Container")
AddBoundaryTag(invisible, $bgColor="transparent",$borderColor="transparent")
Boundary(network, "Any Networks", "Private, Internet, etc") {
Boundary(runnerenv, "Any Environment", "Private,Cloud,etc") {
Container(glrunner, "GitLab Runner") {
Component(glrunnertradconn, "Job Acquisition Connection")
Component(glrunneragtconn, "Agent Connection")
}
}
Boundary(k8senv, "Any Environment", "Private,Cloud,etc") {
Boundary(kasnet, "Any K8s Cluster") {
Component(k8sagent, "GL Agent for K8s")
}
}
Boundary(glinstenv, "Any Environment", "Private,Cloud,etc", $tags="network") {
Container(glinstance, "Any GitLab Instance", "SaaS, Self-Managed") {
Component(kas, "KAS")
Component(glrails, "GitLab Rails",)
}
}
'Opps closing brace in wrong place
}
Rel(glrunnertradconn,glrunneragtconn, "K8s Command", "w/ KUBE_CONTEXT")
'Runner Polling Connection to Job Queue
setIndex(1)
Rel_R(glrunnertradconn, glrails, "Initiates", "SSL", $tags="network")
Rel_R(glrunnertradconn, glrails, "Initiates", "Token Auth", $tags="auth")
Rel_R(glrunnertradconn, glrails, "Control","Poll & Pull Jobs", $tags="control")
'Runner Connection to KAS
setIndex(1)
Rel_R(glrunneragtconn, kas, "Initiates", "SSL", $tags="network")
Rel_R(glrunneragtconn, kas, "Starts", "Token Auth", $tags="auth")
Rel_R(glrunneragtconn, kas, "Control","Push CD K8s CMDs", $tags="control")
'K8s Agent to KAS
setIndex(1)
Rel_R(kas, k8sagent, "Initiates", "SSL", $tags="network")
Rel_R(kas, k8sagent, "Initiates", "Token Auth", $tags="auth")
Rel_R(kas, k8sagent, "Control","Poll & Pull Manifests", $tags="control")
Rel_R(kas, k8sagent, "Control","Push CD K8s CMDs", $tags="control")
'Layouts prioritized in order - put outer most first
Lay_D(glrails,kas)
Lay_R(runnerenv,glinstenv)
Lay_R(glinstenv,k8sagent)
' Boundary(legend3, "Legend", $tags="invisible") {
' note as legend4
' <#white,#white>|<color:$LEGEND_TITLE_COLOR>Legend</color> |
' $showActiveLegendEntries($tagDefaultLegend)
' $showActiveLegendEntries($tagCustomLegend)
' endnote
' }
' Lay_R(glinstenv,legend3)
'SHOW_LEGEND()
center footer \n\nEach Network Environment must provided suitable network routing, DNS and firewall configuration to allow\nthe connection initiation and return conversations indicated in the lines marked with "[Network]" boxes.\n\nThe GitLab Instance, Runners and Kubernetes Agents may all be in the same network environment - the **most complex case** is depicted because \nit would be the norm if using GitLab.com SaaS with self-hosted runners to manage Kubernetes clusters in a variety of other environments.\n\nAll dark orange boxes are part of a GitLab Instance.\n\nOnly the GitLab K8s integration is required for a pure GitOps workflow (depicted as "(G)").
@enduml
The above, along with your explanations has cause me to devise this set of possible ordering rules for building diagrams - would love your review:
'Layout Rendering Priorities
' 1. With dynamic rendering tools (e.g. VS Code plugin) do NOT trust the first rendering
' as it is shifty when adding stuff - even comments. Wait for a bit or close and reopen preview panel.
' 2. Create all components, containers and boundaries first - in order top to bottom or left to right
' 3. Use Rel (directionless) where possible.
' 3. Use Rel
Whew, when I switch from VS Code on Windows to VS Code on Mac I was back to the same problem.
The only way I can resolve it and things seem to make sense is not to have an all enclosing boundary at all - the below works on plantuml.com.
@startuml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Dynamic.puml
'GitLab Colors
' Light Orange: #FCA326, Medium Orange: #FC6D26, Dark Orange: #E24329
'Layout Rendering Priorities
' 1. With dynamic rendering tools (e.g. VS Code plugin) do NOT trust the first rendering
' as it is shifty when adding stuff - even comments. Wait for a bit or close and reopen preview panel.
' 2. Create all components, containers and boundaries first - in order top to bottom or left to right
' 3. Use Rel (directionless) where possible.
' 3. Use Rel_<direction> to force shape layouts.
' 3.a. Order inner objects first when it creates the desired result.
' 3.b. Try NOT to apply order to both inner elements and elements that enclose them.
' 3.c. Make all orderings at the same nesting level whenever possible.
' 4. Add "Lay_<direction>" to force any layouts that Rel_<direction> does not resolve.
' 5. If you have an "All enclosing" boundary, all of the above Rel and Lay statements must
' be inside the "All enclosing" boundary.
' 6. Legend statements must come after at least one usage of each of the elements you want the legend
' to contain.
title GitLab Runner and Kubernetes Agent Connections
AddRelTag(network, $textColor="red", $lineColor="red", $lineStyle = DashedLine(),$legendText="Network Connection")
AddRelTag(auth, $textColor="orange", $lineColor="orange", $lineStyle = DashedLine(),$legendText="Authentication")
AddRelTag(control, $textColor="green", $lineColor="green",$legendText="App Control Flow")
UpdateElementStyle(boundary, $bgColor="transparent",$fontColor="black", $borderColor="gray",$legendText="Network or Environment")
UpdateElementStyle(component,$bgColor="#FC6D26", $borderColor="transparent",$legendText="GitLab Component")
UpdateElementStyle(container,$bgColor="#FCA326", $borderColor="transparent",$legendText="GitLab Container")
AddBoundaryTag(invisible, $bgColor="transparent",$borderColor="transparent")
Boundary(network1, "Any Network", "Private, Internet, etc") {
Boundary(runnerenv, "Any Environment", "Private,Cloud,etc") {
Container(glrunner, "GitLab Runner") {
Component(glrunnertradconn, "Job Acquisition Connection")
Component(glrunneragtconn, "Agent Connection")
}
}
}
Boundary(network2, "Any Network", "Private, Internet, etc") {
Boundary(k8senv, "Any Environment", "Private,Cloud,etc") {
Boundary(kasnet, "Any K8s Cluster") {
Component(k8sagent, "GL Agent for K8s")
}
}
}
Boundary(network3, "Any Network", "Private, Internet, etc") {
Boundary(glinstenv, "Any Environment", "Private,Cloud,etc", $tags="network") {
Container(glinstance, "Any GitLab Instance", "SaaS, Self-Managed") {
Component(kas, "KAS")
Component(glrails, "GitLab Rails",)
}
}
}
Rel(glrunnertradconn,glrunneragtconn, "K8s Command", "w/ KUBE_CONTEXT")
'Runner Polling Connection to Job Queue
setIndex(1)
Rel_R(glrunnertradconn, glrails, "Initiates", "SSL", $tags="network")
Rel_R(glrunnertradconn, glrails, "Initiates", "Token Auth", $tags="auth")
Rel_R(glrunnertradconn, glrails, "Control","Poll & Pull Jobs", $tags="control")
'Runner Connection to KAS
setIndex(1)
Rel_R(glrunneragtconn, kas, "Initiates", "SSL", $tags="network")
Rel_R(glrunneragtconn, kas, "Starts", "Token Auth", $tags="auth")
Rel_R(glrunneragtconn, kas, "Control","Push CD K8s CMDs", $tags="control")
'K8s Agent to KAS
setIndex(1)
Rel_R(kas, k8sagent, "Initiates", "SSL", $tags="network")
Rel_R(kas, k8sagent, "Initiates", "Token Auth", $tags="auth")
Rel_R(kas, k8sagent, "Control","Poll & Pull Manifests", $tags="control")
Rel_R(kas, k8sagent, "Control","Push CD K8s CMDs", $tags="control")
'Layouts prioritized in order - put outer most first
Lay_D(glrails,kas)
Lay_R(runnerenv,glinstenv)
Lay_R(glinstenv,k8sagent)
' Boundary(legend3, "Legend", $tags="invisible") {
' note as legend4
' <#white,#white>|<color:$LEGEND_TITLE_COLOR>Legend</color> |
' $showActiveLegendEntries($tagDefaultLegend)
' $showActiveLegendEntries($tagCustomLegend)
' endnote
' }
' Lay_R(glinstenv,legend3)
SHOW_FLOATING_LEGEND()
center footer \n\nEach Network Environment must provided suitable network routing, DNS and firewall configuration to allow\nthe connection initiation and return conversations indicated in the lines marked with "[Network]" boxes.\n\nThe GitLab Instance, Runners and Kubernetes Agents may all be in the same network environment - the **most complex case** is depicted because \nit would be the norm if using GitLab.com SaaS with self-hosted runners to manage Kubernetes clusters in a variety of other environments.\n\nAll dark orange boxes are part of a GitLab Instance.\n\nOnly the GitLab K8s integration is required for a pure GitOps workflow (depicted as "(G)").
@enduml
Whew. :sweat_smile:
There is a lot of useful information here! that hard part for me is: how do we document this (and where?) so that these sorts of scenarios are easily explained and usable for others? :thinking:
@Potherca - by a pull request of course 😄 : https://github.com/plantuml-stdlib/C4-PlantUML/pull/239
I posted this screenshot and the source 3 days ago: https://github.com/plantuml-stdlib/C4-PlantUML/issues/236
And now it is rendering like this - no matter what I do with Rel and Lay - I can't get back to the expected rendering depicted in that issue.
Source
```plantuml @startuml !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml 'LAYOUT_LANDSCAPE() title GitLab Runner and Kubernetes Agent Connections AddRelTag("network", $textColor="red", $lineColor="red", $lineStyle = DashedLine()) AddRelTag("auth", $textColor="orange", $lineColor="orange", $lineStyle = DashedLine()) AddRelTag("control", $textColor="green", $lineColor="green") Boundary(network, "Any Networks", "Private, Internet, etc") { Boundary(runnerenv, "Any Environment", "Private,Cloud,etc") { Container(glrunner, "GitLab Runner") { Component(glrunnertradconn, "Job Acquisition Connection") Component(glrunneragtconn, "Agent Connection") Lay_D(glrunnertradconn,glrunneragtconn) } } Boundary(glinstenv, "Any Environment", "Private,Cloud,etc") { Boundary(glinstance, "Any GitLab Instance", "SaaS, Self-Managed") { Component(kas, "KAS", "Spring Bean") Component(glrails, "GitLab Rails",) } } Rel(glrunnertradconn,glrunneragtconn, "K8s Command", "w/ KUBE_CONTEXT") 'Runner Polling Connection to Job Queue Rel_R(glrunnertradconn, glrails, "Initiates", "SSL", $tags="network") Rel_R(glrunnertradconn, glrails, "Initiates", "Token Auth", $tags="auth") Rel_L(glrails, glrunnertradconn, "Control","Poll & Pull Jobs", $tags="control") Boundary(k8senv, "Any Environment", "Private,Cloud,etc") { Boundary(kasnet, "Any K8s Cluster") { Component(k8sagent, "GL Agent for K8s") } } 'Runner Connection to KAS Rel_R(glrunneragtconn, kas, "Initiates", "SSL", $tags="network") Rel_R(glrunneragtconn, kas, "Control","Push K8s CMDs", $tags="control") Rel_R(glrunneragtconn, kas, "Starts", "Token Auth", $tags="auth") 'K8s Agent to KAS Rel_L(k8sagent, kas, "Initiates", "SSL", $tags="network") Rel_L(k8sagent, kas, "Initiates", "Token Auth", $tags="auth") Rel_R(kas, k8sagent, "Control","Poll & Pull Manifests", $tags="control") 'Layouts prioritized in order - put outer most first Lay_R(runnerenv,glinstenv) Lay_R(glinstenv,k8sagent) Lay_D(glrails,kas) 'Rel_R(glrunner,kas, "Control", "Push CD", $tags="control") 'Rel_R(kas,k8sagent, "Control", "Push CD", $tags="control") } SHOW_LEGEND() @enduml ```