document models and admin
All checks were successful
Build and Deploy Staging / build (push) Successful in 1m12s
Build and Deploy Antora Docs / build (push) Successful in 40s
Tests / test (push) Successful in 24s
Build and Deploy Staging / deploy (push) Successful in 10s
Build and Deploy Antora Docs / deploy (push) Successful in 5s

This commit is contained in:
Tobias Brunner 2025-04-15 10:36:08 +02:00
parent fa3eb7c4fc
commit c4f7c8df69
No known key found for this signature in database
7 changed files with 860 additions and 56 deletions

View file

@ -0,0 +1,469 @@
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" style="background: transparent; background-color: transparent;" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="981px" height="521px" viewBox="-0.5 -0.5 981 521" content="&lt;mxfile&gt;&lt;diagram id=&quot;iQWVNTns26_xaYTS_M6j&quot; name=&quot;Page-1&quot;&gt;7Vtbc+MmFP41nmkf6pGEbn7c2Gm3M+k2Hc9su49YQjKtLFyMb/vrCxboir2KLUvJNslDxOEi+L7DOYcjMgLT1eEXCtfL30iIkpFlhIcRmI0syzQ9m/8RkmMm8XyQCWKKQ9moEMzxVySFhpRucYg2lYaMkIThdVUYkDRFAavIIKVkX20WkaT61jWMUUMwD2DSlP6JQ7bMpL5jFPKPCMdL9WbTkDUrqBpLwWYJQ7IvicDjCEwpISx7Wh2mKBHgKVyyfj+fqc0nRlHK2nSwsg47mGzl2qYJ2YZc9EzJjqNMR2IE99+tmNHD44FsTjDkErkMdlTYULJNQySGN3n1fokZmq9hIGr3XBu4bMlWiazeIcowH/BDguOUyxaEMbLiFRFOkilJCD0NCiJH/Ao5SVlJnv1w+YZR8g8q1binn3yC4kXocBYkM4ee6ywiK8TokTeRHVzFllRXWxG9L8i3XClblolXvEOpcHE+dsEJf5C06CkCL6PoVPk/JCk3DhdIAv69SLKbJHEQKBFtnhOYcjIM8wINxrdp6AIjUMPIa2KUw1HGyO4AIqcFRNbgEAFnQIjcFhB9GhyiHJIhIPIay0ch982ySChbkpikMHkspA9VgIo2T4SsJSx/I8aOMtCAW0aqoHFg6PEv0X/sqOIXOdypMDtUSkdVOmCWdbMcWfyihuTPRS9RUJ2yBYpVXeaHg0C2NJCtJpmIQRoj1crS00hRAhneVYe/hRO/oba/rtYJWvH5IuGnFkctaU9wwePCCtBQuphAdKUa37PCYZhxijb4K1ycxhOorQlO2WkNzsPImV3SdBkVys55iFRB2Lu4A34yxsBQkB8rg7cGVw7+LOZdGlmNqrqQKNpwQuts5HNqRdCkQdAc0R0ORIiQiAAhxDv+GIvHH9A4Hgt7QzYspmj+x9OPqhV/TanhbUFFLXYIHeSHti5G8K0F6CpG8AyvwhnQGC5LY7jcDgyXCk9KJAxhyq6zLqalMS9uT+bFbAZOfdj83HZ77W33tY6iO1ayMKsPVppHx9yoGDMU4RQzTNJWFkb6COPz/OOnPqxNFCE3CHTWJvQmC8PoxtrYtWOjaWuOjeBe5gYMu2nuHe+YblP37RtV/9T1A6XwWGogA4uzHts2qiwDr5ZxqbW3/Ivt+UM2g2t9vWm/FeJ7NpYahenNVjrvnLTl5NaoopdN7Fh33sTNTEDhXH/n7oviNH6xaxWAiEUUKcJeQnuI/EjrbN3AR4uoo9RWzaxamqREnoDo3NkOk5V4/fvb1wSofl9Gt5mWeGGAOnuAcN77Ubif4NS3hwxOJ4Psl5Lu+6Ci/WPD+cYGOJWeud3lixWZqVp+7wVnxOu2ki6/19dOspqpizef4MtU8Oz24BrhqSNUNwm+ThN66r2v/HuTaw/4pUDl1175ByfPHRKjYbIEvQYh6sNjJQg5w0n3pvP9NN6eFKsvUpqfojs8Xak7Mt/P2coFA56t1EfxN7WDvD62kO5wdatduyp7Yhq1GwHqEsW59ImnEmL69jenTwa6JfBGdeZWs9uPzvjgzjpzKV3Qwik0kwXfr0OYOEM6hLO3GS6wJCN/GfbradB1inC8pbCcJ7pn5kddoDx/UfLc1coQbpb5Wztg2KonuCeaU4mpYdjpgGHQzDe8M9w1w8AekuFm/uKd4a4Ztt3+GObF4p8WMu9b/OsHePwP&lt;/diagram&gt;&lt;/mxfile&gt;">
<defs/>
<g>
<g>
<rect x="410" y="420" width="260" height="100" rx="15" ry="15" fill="#f5f5f5" stroke="#666666" pointer-events="all" style="fill: light-dark(rgb(245, 245, 245), rgb(26, 26, 26)); stroke: light-dark(rgb(102, 102, 102), rgb(149, 149, 149));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 258px; height: 1px; padding-top: 517px; margin-left: 411px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #333333; ">
<div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#333333, #c1c1c1); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
Cloud Provider "Exoscale"
</div>
</div>
</div>
</foreignObject>
<text x="540" y="517" fill="#333333" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">
Cloud Provider "Exoscale"
</text>
</switch>
</g>
</g>
<g>
<rect x="0" y="420" width="380" height="100" rx="15" ry="15" fill="#f5f5f5" stroke="#666666" pointer-events="all" style="fill: light-dark(rgb(245, 245, 245), rgb(26, 26, 26)); stroke: light-dark(rgb(102, 102, 102), rgb(149, 149, 149));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 378px; height: 1px; padding-top: 517px; margin-left: 1px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #333333; ">
<div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#333333, #c1c1c1); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
Cloud Provider "Cloudscale"
</div>
</div>
</div>
</foreignObject>
<text x="190" y="517" fill="#333333" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">
Cloud Provider "Cloudscale"
</text>
</switch>
</g>
</g>
<g>
<rect x="20" y="440" width="100" height="40" fill="#ffffff" stroke="#000000" pointer-events="all" style="fill: light-dark(#ffffff, var(--ge-dark-color, #121212)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 460px; margin-left: 21px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; ">
<div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
Control Plane 1
</div>
</div>
</div>
</foreignObject>
<text x="70" y="464" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">
Control Plane 1
</text>
</switch>
</g>
</g>
<g>
<rect x="140" y="440" width="100" height="40" fill="#ffffff" stroke="#000000" pointer-events="all" style="fill: light-dark(#ffffff, var(--ge-dark-color, #121212)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 460px; margin-left: 141px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; ">
<div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
Control Plane 2
</div>
</div>
</div>
</foreignObject>
<text x="190" y="464" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">
Control Plane 2
</text>
</switch>
</g>
</g>
<g>
<rect x="260" y="440" width="100" height="40" fill="#ffffff" stroke="#000000" pointer-events="all" style="fill: light-dark(#ffffff, var(--ge-dark-color, #121212)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 460px; margin-left: 261px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; ">
<div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
Control Plane N
</div>
</div>
</div>
</foreignObject>
<text x="310" y="464" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">
Control Plane N
</text>
</switch>
</g>
</g>
<g>
<path d="M 527 60 L 527 85 L 325 85 L 325 103.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
<path d="M 325 108.88 L 321.5 101.88 L 325 103.63 L 328.5 101.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 87px; margin-left: 436px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; background-color: #ffffff; ">
<div style="display: inline-block; font-size: 11px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; background-color: light-dark(#ffffff, var(--ge-dark-color, #121212)); white-space: nowrap; ">
Implemented by
</div>
</div>
</div>
</foreignObject>
<text x="436" y="90" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="11px" text-anchor="middle">
Implemented by
</text>
</switch>
</g>
</g>
<g>
<rect x="497" y="0" width="120" height="60" rx="9" ry="9" fill="#d5e8d4" stroke="#82b366" pointer-events="all" style="fill: light-dark(rgb(213, 232, 212), rgb(31, 47, 30)); stroke: light-dark(rgb(130, 179, 102), rgb(68, 110, 44));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 30px; margin-left: 498px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; ">
<div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
Service
<div>
(e.g. PostgreSQL)
</div>
</div>
</div>
</div>
</foreignObject>
<text x="557" y="34" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">
Service...
</text>
</switch>
</g>
</g>
<g>
<path d="M 325 170 L 325 205 L 190 205 L 190 233.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
<path d="M 190 238.88 L 186.5 231.88 L 190 233.63 L 193.5 231.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
</g>
<g>
<path d="M 382.5 170 L 382.5 205 L 540 205 L 540 233.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
<path d="M 540 238.88 L 536.5 231.88 L 540 233.63 L 543.5 231.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
</g>
<g>
<rect x="210" y="110" width="230" height="60" rx="9" ry="9" fill="#ffe6cc" stroke="#d79b00" pointer-events="all" style="fill: light-dark(rgb(255, 230, 204), rgb(54, 33, 10)); stroke: light-dark(rgb(215, 155, 0), rgb(153, 101, 0));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 228px; height: 1px; padding-top: 140px; margin-left: 211px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; ">
<div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
Service Definition
<div>
(e.g. PostgreSQL by VSHN)
</div>
</div>
</div>
</div>
</foreignObject>
<text x="325" y="144" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">
Service Definition...
</text>
</switch>
</g>
</g>
<g>
<path d="M 190 300 L 190 340 L 70 340 L 70 433.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
<path d="M 70 438.88 L 66.5 431.88 L 70 433.63 L 73.5 431.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
</g>
<g>
<path d="M 190 300 L 190 433.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
<path d="M 190 438.88 L 186.5 431.88 L 190 433.63 L 193.5 431.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
</g>
<g>
<path d="M 190 300 L 190 340 L 310 340 L 310 433.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
<path d="M 310 438.88 L 306.5 431.88 L 310 433.63 L 313.5 431.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
</g>
<g>
<rect x="70" y="240" width="240" height="60" rx="9" ry="9" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all" style="fill: light-dark(rgb(218, 232, 252), rgb(29, 41, 59)); stroke: light-dark(rgb(108, 142, 191), rgb(92, 121, 163));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 238px; height: 1px; padding-top: 270px; margin-left: 71px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; ">
<div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
Service Offering
<div>
(e.g. PostgreSQL by VSHN at Cloudscale)
</div>
</div>
</div>
</div>
</foreignObject>
<text x="190" y="274" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">
Service Offering...
</text>
</switch>
</g>
</g>
<g>
<path d="M 745 170 L 745 205 L 860 205 L 860 233.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
<path d="M 860 238.88 L 856.5 231.88 L 860 233.63 L 863.5 231.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
</g>
<g>
<rect x="630" y="110" width="230" height="60" rx="9" ry="9" fill="#ffe6cc" stroke="#d79b00" pointer-events="all" style="fill: light-dark(rgb(255, 230, 204), rgb(54, 33, 10)); stroke: light-dark(rgb(215, 155, 0), rgb(153, 101, 0));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 228px; height: 1px; padding-top: 140px; margin-left: 631px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; ">
<div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
Service Definition
<div>
(e.g. DBaaS PostgreSQL)
</div>
</div>
</div>
</div>
</foreignObject>
<text x="745" y="144" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">
Service Definition...
</text>
</switch>
</g>
</g>
<g>
<path d="M 587 60 L 587 85 L 764.1 85 L 764.09 106.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
<path d="M 764.09 111.88 L 760.59 104.88 L 764.09 106.63 L 767.59 104.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 85px; margin-left: 686px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; background-color: #ffffff; ">
<div style="display: inline-block; font-size: 11px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; background-color: light-dark(#ffffff, var(--ge-dark-color, #121212)); white-space: nowrap; ">
Implemented by
</div>
</div>
</div>
</foreignObject>
<text x="686" y="88" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="11px" text-anchor="middle">
Implemented by
</text>
</switch>
</g>
</g>
<g>
<rect x="430" y="440" width="100" height="40" fill="#ffffff" stroke="#000000" pointer-events="all" style="fill: light-dark(#ffffff, var(--ge-dark-color, #121212)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 460px; margin-left: 431px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; ">
<div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
Control Plane 1
</div>
</div>
</div>
</foreignObject>
<text x="480" y="464" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">
Control Plane 1
</text>
</switch>
</g>
</g>
<g>
<rect x="550" y="440" width="100" height="40" fill="#ffffff" stroke="#000000" pointer-events="all" style="fill: light-dark(#ffffff, var(--ge-dark-color, #121212)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 460px; margin-left: 551px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; ">
<div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
Control Plane 2
</div>
</div>
</div>
</foreignObject>
<text x="600" y="464" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">
Control Plane 2
</text>
</switch>
</g>
</g>
<g>
<path d="M 540 300 L 540 370 L 480 370 L 480 433.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
<path d="M 480 438.88 L 476.5 431.88 L 480 433.63 L 483.5 431.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
</g>
<g>
<path d="M 540 300 L 540 370 L 600 370 L 600 433.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
<path d="M 600 438.88 L 596.5 431.88 L 600 433.63 L 603.5 431.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
</g>
<g>
<rect x="420" y="240" width="240" height="60" rx="9" ry="9" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all" style="fill: light-dark(rgb(218, 232, 252), rgb(29, 41, 59)); stroke: light-dark(rgb(108, 142, 191), rgb(92, 121, 163));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 238px; height: 1px; padding-top: 270px; margin-left: 421px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; ">
<div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
Service Offering
<div>
(e.g. PostgreSQL by VSHN at Exoscale)
</div>
</div>
</div>
</div>
</foreignObject>
<text x="540" y="274" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">
Service Offering...
</text>
</switch>
</g>
</g>
<g>
<path d="M 860 300 L 860 320 L 505 320 L 505 433.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
<path d="M 505 438.88 L 501.5 431.88 L 505 433.63 L 508.5 431.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
</g>
<g>
<path d="M 860 300 L 860 320 L 625 320 L 625 433.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
<path d="M 625 438.88 L 621.5 431.88 L 625 433.63 L 628.5 431.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
</g>
<g>
<rect x="740" y="240" width="240" height="60" rx="9" ry="9" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all" style="fill: light-dark(rgb(218, 232, 252), rgb(29, 41, 59)); stroke: light-dark(rgb(108, 142, 191), rgb(92, 121, 163));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 238px; height: 1px; padding-top: 270px; margin-left: 741px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; ">
<div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
Service Offering
<div>
(e.g. DBaaS PostgreSQL at Exoscale)
</div>
</div>
</div>
</div>
</foreignObject>
<text x="860" y="274" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">
Service Offering...
</text>
</switch>
</g>
</g>
<g>
<rect x="10" y="360" width="110" height="50" rx="7.5" ry="7.5" fill="#f5f5f5" stroke="#666666" stroke-dasharray="3 3" pointer-events="all" style="fill: light-dark(rgb(245, 245, 245), rgb(26, 26, 26)); stroke: light-dark(rgb(102, 102, 102), rgb(149, 149, 149));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 385px; margin-left: 11px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #333333; ">
<div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#333333, #c1c1c1); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
ServiceOffering
<div>
ControlPlane
</div>
<div>
Configuration
</div>
</div>
</div>
</div>
</foreignObject>
<text x="65" y="389" fill="#333333" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">
ServiceOffering...
</text>
</switch>
</g>
</g>
<g>
<rect x="130" y="360" width="110" height="50" rx="7.5" ry="7.5" fill="#f5f5f5" stroke="#666666" stroke-dasharray="3 3" pointer-events="all" style="fill: light-dark(rgb(245, 245, 245), rgb(26, 26, 26)); stroke: light-dark(rgb(102, 102, 102), rgb(149, 149, 149));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 385px; margin-left: 131px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #333333; ">
<div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#333333, #c1c1c1); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
ServiceOffering
<div>
ControlPlane
</div>
<div>
Configuration
</div>
</div>
</div>
</div>
</foreignObject>
<text x="185" y="389" fill="#333333" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">
ServiceOffering...
</text>
</switch>
</g>
</g>
<g>
<rect x="250" y="360" width="110" height="50" rx="7.5" ry="7.5" fill="#f5f5f5" stroke="#666666" stroke-dasharray="3 3" pointer-events="all" style="fill: light-dark(rgb(245, 245, 245), rgb(26, 26, 26)); stroke: light-dark(rgb(102, 102, 102), rgb(149, 149, 149));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 385px; margin-left: 251px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #333333; ">
<div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#333333, #c1c1c1); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
ServiceOffering
<div>
ControlPlane
</div>
<div>
Configuration
</div>
</div>
</div>
</div>
</foreignObject>
<text x="305" y="389" fill="#333333" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">
ServiceOffering...
</text>
</switch>
</g>
</g>
</g>
<switch>
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
<a transform="translate(0,-5)" xlink:href="https://www.drawio.com/doc/faq/svg-export-text-problems" target="_blank">
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
Text is not SVG - cannot display
</text>
</a>
</switch>
</svg>

After

Width:  |  Height:  |  Size: 38 KiB

View file

@ -0,0 +1,226 @@
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" style="background: transparent; background-color: transparent;" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="741px" height="211px" viewBox="-0.5 -0.5 741 211" content="&lt;mxfile&gt;&lt;diagram id=&quot;oCg05mIILSXJTHsOPe0H&quot; name=&quot;Page-1&quot;&gt;5VjbctowEP0aHpvxHecxQJp0knbokDZN34QtjCbCYmRxy9d3hSVsWaYhhaS3J2tX0lo6e/Zid/z+bH3F0Xz6kaWYdjwnXXf8QcfzXLcbwENqNqWmG/ulIuMkVYsqxYg8YaV0lHZBUlwYCwVjVJC5qUxYnuNEGDrEOVuZyyaMmm+dowxbilGCqK29J6mYlto4dCr9NSbZVL/ZddTMDOnFSlFMUcpWNZV/2fH7nDFRjmbrPqYSPI1Lue/9ntndwTjOxSEbglidQ2z05XAKd1ViznJ49KZiRkFyYQh2+eYbCM5ZqMUHKWphsDakjZbWRNS2gfSgLcK42iQFvce+jLpfwRY8Ucf1lP8Rz7BaFZYqeZHaNgXAFWYzDCeDBRxTJMjSdCpS3Mh26yr4YKAQbEczikobS0QXyupNXIDiYvihFedbNIbQMABGlGQ5jBO4NOagWGIuCHDvQk3MSJpKGz2OC/KExlt7Eq45I7nYnj7sdcJBK4Da39IoXndagkUZNPhooKZ2vXPOPDdU5jaGpYNxVcaH8uA1y7twUWa1Q7UJNpkU4OumY3ZnPMhXcfRS5isGuzX+VmxuZ3AVLUasVKGzJ1paCfwroRAHb0T9uPtfwOm9EZyelUju8RgUQ8YFohbUnC3yFKcKztWUCDyao+1FVlCCTeQnhNI+o4xv9/opwvEkAX0hOHvEtZkoifF48rNEbKWRvenCC8yYdnWxXFUFNNBrprXiGYbHoxlYaF7f3Q1HL2TsXhTqDPH2cK1OLP9IErWnzfMGwN3YtFCeQG06KnX6FpxfCihVTTSht5nL4WJGLxLB6rVsW/eGrCCCMFnTBJu3VLoxE4LNTCewhaAkB5bqzk7G95Rx8sRyGRnlKtqwvyumVnU9AbXj55nttxA7co4ndmh5oj8aggIO7HyXBIbWo+NFFF7ZG4OHokyOADwIdWlnSJFcdMpsEuI4DdqySeyNfWiQTpNNokaH0LUxd1uzySlAd/ag7v3jqIfu70T9xQ3G3/Cl4rSWi5PXBr+RoyBJHVQcLEPNMg5931nDVHm/U9SZqGsF2h/2OaVJefznlHMWBPq3yMZI7kd/TfnmltN8PdkNqm5NnUFPJ7+ULHX2g2IsMo5Hn2/1JLyiNr+3e0iRQAV0D/j5pHiCFNf1G8X83E5x569UzGO7Tf06uv4k+Z4kUBxEC6w3eJNQhh5fG9QWgls4Hw6q+2qgglj9PCt5Xf2C9C9/AA==&lt;/diagram&gt;&lt;/mxfile&gt;">
<defs/>
<g>
<g>
<path d="M 380 95 L 255.7 157.15" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
<path d="M 251 159.5 L 255.7 153.24 L 255.7 157.15 L 258.83 159.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 147px; margin-left: 230px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; background-color: #ffffff; ">
<div style="display: inline-block; font-size: 11px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; background-color: light-dark(#ffffff, var(--ge-dark-color, #121212)); white-space: nowrap; ">
K8s API
</div>
</div>
</div>
</foreignObject>
<text x="230" y="150" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="11px" text-anchor="middle">
K8s API
</text>
</switch>
</g>
</g>
<g>
<path d="M 600 67.5 L 644.91 33.82" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
<path d="M 649.11 30.67 L 645.61 37.67 L 644.91 33.82 L 641.41 32.07 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
</g>
<g>
<path d="M 600 67.5 L 645.15 105.88" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
<path d="M 649.15 109.28 L 641.55 107.41 L 645.15 105.88 L 646.08 102.08 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
</g>
<g>
<rect x="160" y="40" width="440" height="55" rx="8.25" ry="8.25" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all" style="fill: light-dark(rgb(218, 232, 252), rgb(29, 41, 59)); stroke: light-dark(rgb(108, 142, 191), rgb(92, 121, 163));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 438px; height: 1px; padding-top: 68px; margin-left: 161px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; ">
<div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
Web Portal
</div>
</div>
</div>
</foreignObject>
<text x="380" y="71" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">
Web Portal
</text>
</switch>
</g>
</g>
<g>
<path d="M 30 69.9 L 153.63 69.05" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
<path d="M 158.88 69.01 L 151.91 72.56 L 153.63 69.05 L 151.86 65.56 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 69px; margin-left: 95px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; background-color: #ffffff; ">
<div style="display: inline-block; font-size: 11px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; background-color: light-dark(#ffffff, var(--ge-dark-color, #121212)); white-space: nowrap; ">
HTTPS
</div>
</div>
</div>
</foreignObject>
<text x="95" y="73" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="11px" text-anchor="middle">
HTTPS
</text>
</switch>
</g>
</g>
<g>
<ellipse cx="15" cy="47.5" rx="7.5" ry="7.5" fill="#ffffff" stroke="#000000" pointer-events="all" style="fill: light-dark(#ffffff, var(--ge-dark-color, #121212)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
<path d="M 15 55 L 15 80 M 15 60 L 0 60 M 15 60 L 30 60 M 15 80 L 0 100 M 15 80 L 30 100" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 37px; margin-left: 15px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; ">
<div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: nowrap; ">
User
</div>
</div>
</div>
</foreignObject>
<text x="15" y="37" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">
User
</text>
</switch>
</g>
</g>
<g>
<rect x="180" y="160" width="140" height="50" rx="7.5" ry="7.5" fill="#d5e8d4" stroke="#82b366" pointer-events="all" style="fill: light-dark(rgb(213, 232, 212), rgb(31, 47, 30)); stroke: light-dark(rgb(130, 179, 102), rgb(68, 110, 44));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 138px; height: 1px; padding-top: 185px; margin-left: 181px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; ">
<div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
CSP 1 Zone A
<br/>
Control Plane
</div>
</div>
</div>
</foreignObject>
<text x="250" y="189" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">
CSP 1 Zone A...
</text>
</switch>
</g>
</g>
<g>
<rect x="430" y="160" width="140" height="50" rx="7.5" ry="7.5" fill="#d5e8d4" stroke="#82b366" pointer-events="all" style="fill: light-dark(rgb(213, 232, 212), rgb(31, 47, 30)); stroke: light-dark(rgb(130, 179, 102), rgb(68, 110, 44));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 138px; height: 1px; padding-top: 185px; margin-left: 431px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; ">
<div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
CSP 2 Zone A
<br/>
Control Plane
</div>
</div>
</div>
</foreignObject>
<text x="500" y="189" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">
CSP 2 Zone A...
</text>
</switch>
</g>
</g>
<g>
<path d="M 380 95 L 494.4 156.97" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
<path d="M 499.02 159.47 L 491.19 159.21 L 494.4 156.97 L 494.53 153.06 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 138px; margin-left: 457px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; background-color: #ffffff; ">
<div style="display: inline-block; font-size: 11px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; background-color: light-dark(#ffffff, var(--ge-dark-color, #121212)); white-space: nowrap; ">
K8s API
</div>
</div>
</div>
</foreignObject>
<text x="457" y="142" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="11px" text-anchor="middle">
K8s API
</text>
</switch>
</g>
</g>
<g>
<path d="M 650 88 C 650 77.33 740 77.33 740 88 L 740 132 C 740 142.67 650 142.67 650 132 Z" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(#ffffff, var(--ge-dark-color, #121212)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
<path d="M 650 88 C 650 96 740 96 740 88 M 650 92 C 650 100 740 100 740 92 M 650 96 C 650 104 740 104 740 96" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 120px; margin-left: 651px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; ">
<div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
Portal DB
<div>
PostgreSQL
</div>
</div>
</div>
</div>
</foreignObject>
<text x="695" y="124" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">
Portal DB...
</text>
</switch>
</g>
</g>
<g>
<path d="M 650 8 C 650 -2.67 740 -2.67 740 8 L 740 52 C 740 62.67 650 62.67 650 52 Z" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(#ffffff, var(--ge-dark-color, #121212)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
<path d="M 650 8 C 650 16 740 16 740 8 M 650 12 C 650 20 740 20 740 12 M 650 16 C 650 24 740 24 740 16" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 40px; margin-left: 651px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; ">
<div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
VSHN Account
<div>
Keycloak
</div>
</div>
</div>
</div>
</foreignObject>
<text x="695" y="44" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">
VSHN Account...
</text>
</switch>
</g>
</g>
</g>
<switch>
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
<a transform="translate(0,-5)" xlink:href="https://www.drawio.com/doc/faq/svg-export-text-problems" target="_blank">
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
Text is not SVG - cannot display
</text>
</a>
</switch>
</svg>

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -2,6 +2,10 @@
* xref:terminology.adoc[]
* xref:web-portal.adoc[]
** xref:web-portal-admin.adoc[Admin]
** xref:web-portal-controlplanes.adoc[Control-Planes]
* xref:web-portal-planning.adoc[]
** xref:user-stories.adoc[]
** xref:organizations.adoc[]
** xref:authentication.adoc[]
@ -9,7 +13,6 @@
** xref:service-catalog.adoc[]
** xref:service-instances.adoc[]
** xref:api.adoc[]
** xref:database-diagram.adoc[]
* Cloud Providers
** xref:exoscale-osb.adoc[]

View file

@ -0,0 +1,59 @@
= Web Portal Admin
The administration of the web portal happens with Django Admin.
[TIP]
====
* Production: https://portal.servala.com/admin[portal.servala.com^]
* Staging: https://staging.portal.servala.com/admin[staging.portal.servala.com^]
====
== Service Catalog and Control-Plane Models
image::portal-service-relations.drawio.svg[]
Service::
The software service, top-level, categorized. +
_Examples_: PostgreSQL, Redis, GitLab. +
Admin: https://staging.portal.servala.com/admin/core/service/[staging^], https://portal.servala.com/admin/core/service/[prod^]
Service Definition::
A correlation between a specific managed service offering with the API definition on the control-planes. It tells the Portal which Kubernetes API implements a managed service. +
_Example_: "Forgejo by VSHN" is implemented by GVK `vshn.appcat.vshn.io/v1/VSHNForgejo` on the control-planes. +
Admin: https://staging.portal.servala.com/admin/core/servicedefinition/[staging^], https://portal.servala.com/admin/core/servicedefinition/[prod^]
Service Offering::
The service offering is the glue which connects a service with a service provider, the control-planes with the service definitions and plan information. It essentially tells the Portal which managed service is available on which control-plane with which specific configuration. It relates to "ControlPlane CRD" which is a correlation between "Service Offering", "Control Plane" and "Service Definition".
_Example_: "Forgejo at Hetzner Cloud" which makes the Service "Forgejo" available at Hetzner Cloud and through "ControlPlane CRDs" it defines which service definition is available in which control-plane at Hetzner Cloud. It also specifies plans with features, pricing and terms. +
Admin: https://staging.portal.servala.com/admin/core/serviceoffering/[staging^], https://portal.servala.com/admin/core/serviceoffering/[prod^]
== Models
In addition to the models described in <<Service Catalog and Control-Plane Models>>, the following core models exist:
Cloud Providers::
Cloud providers where service instances can be provisioned at.
Control Planes::
Connections to Kubernetes API servers. Each control-plane represents a zone at a cloud provider.
Organizations::
The main multi-tenant object.
Organization Memberships::
Defines organization memberships including the roles in an organization.
Organization Origins::
The origin of an organization. Where the organization is coming from, influences e.g. access to control-planes or service offerings.
Billing Entities::
Billing contacts for Organizations - this is not further implemented yet.
Plans::
Plans for service offerings.
Service Categories::
Allows to categorize services.
Service Instances::
Service instances provisioned on control-planes.

View file

@ -0,0 +1,30 @@
= Web Portal Control-Planes
Each control-plane represents a zone at a cloud provider. It's a dedicated Kubernetes API endpoint running the Servala control-plane.
To register a control-plane, a service account with appropriate permissions is required on the Kubernetes API server.
Example:
[source,bash]
----
# Create service account
kubectl -n kube-system create sa servala-portal
# Create long-lived token for service account
kubectl -n kube-system apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
name: servala-portal-token
annotations:
kubernetes.io/service-account.name: servala-portal
type: kubernetes.io/service-account-token
EOF
# Grant access
kubectl create clusterrolebinding servala-portal-admin --clusterrole=cluster-admin --serviceaccount=kube-system:servala-portal
# Retrieve token
kubectl ksd -n kube-system get secret servala-portal-token -o yaml
----

View file

@ -0,0 +1,64 @@
= Web Portal Planning
image::web-portal-arch.drawio.svg[]
The Servala Web Portal is the central multi-tenant multi-service-provider aggregation and self-service main entrypoint for Servala.
Servala is the brand for the product formerly known as "VSHN Application Marketplace".
It offers self-service provisioning, multi-tenancy via organizations, access control, and provides central management access over multi cloud providers and the instances running this way.
The portal is a web application consuming various third party APIs to provide an aggregated and opinionated view.
External resources to read about it:
* http://vshn.ch/marketplace[vshn.ch Website^]
* https://products.vshn.ch/marketplace/index.html[VSHN products site^]
The source code can be found on the https://servala.app.codey.ch/servala/servala-portal[Servala Codey instance^].
== Technology Stack
We choose:
* Python https://docs.djangoproject.com/en/dev/internals/release-process/#term-Long-term-support-release[Django LTS^]
* PostgreSQL as database backend
* https://gunicorn.org/[Gunicorn^] Python WSGI HTTP Server
* https://caddyserver.com/[Caddy^] for serving static files and WSGI, or https://whitenoise.readthedocs.io/en/latest/[WhiteNoise^] (TBD)
* https://docs.astral.sh/uv/[Astral uv^] for Python project, dependency and build management
* https://htmx.org/[htmx] for the dynamic part in the frontend
* https://getbootstrap.com/[Bootstrap 5] for styling the frontend
A complete reasoning for this stack is available in https://vshnwiki.atlassian.net/wiki/spaces/VSHNPM/pages/402718747/Self-Service+Marketplace+Web+Application[our wiki^] (internal page).
== Development Paradigms
Keep usage of third-party dependencies low::
Every external dependency adds a burden on the maintenance of the application.
Adding a dependency must be done with care:
* Is the dependency well maintained and adopted in the ecosystem?
* Could we do it without the dependency? If not, why?
* What happens if the dependency is abandoned?
* Document the reason for each dependency, why it has been chosen and why we can't live without it.
Graceful degradation::
The application will connect to various upstream APIs which we can't control.
Should issues arise with one of these APIs, gracefully degrade the feature set and inform accordingly ("This service is currently not available - we're working on it").
Never must the application crash because and upstream API not being reachable, has slow response time or react in an undefined way.
Use database and caches wisely::
External systems like databases, caches or queues add additional complexity and burden to the application operations.
Every additional system must be chosen carefully and the reasoning documented.
Alternatives must be considered and documented.
Business logic vs. views::
Whenever possible, split business logic from views.
This allows to progress the application in the future to allow for different views (For example APIs or other alternative frontends).
Django specifics::
* We use class based views by default, exceptions can be made
* Dynamic configuration happens via environment variables
* Different environments (dev / test / prod) must be clearly separated inside the application
Testing::
Business functionality must be https://docs.djangoproject.com/en/5.1/topics/testing/[tested^] with code.
We preferrably use https://docs.pytest.org/[pytest^].

View file

@ -1,64 +1,17 @@
= Web Portal
image::web-portal-arch.drawio.svg[]
image::web-portal-arch-current.drawio.svg[]
[TIP]
====
* Production: https://portal.servala.com/[portal.servala.com^]
* Staging: https://staging.portal.servala.com/[staging.portal.servala.com^]
====
The Servala Web Portal is the central multi-tenant multi-service-provider aggregation and self-service main entrypoint for Servala.
Servala is the brand for the product formerly known as "VSHN Application Marketplace".
It offers self-service provisioning, multi-tenancy via organizations, access control, and provides central management access over multi cloud providers and the instances running this way.
The portal is a web application consuming various third party APIs to provide an aggregated and opinionated view.
External resources to read about it:
* http://vshn.ch/marketplace[vshn.ch Website^]
* https://products.vshn.ch/marketplace/index.html[VSHN products site^]
The source code can be found on the https://servala.app.codey.ch/servala/servala-portal[Servala Codey instance^].
== Technology Stack
We choose:
* Python https://docs.djangoproject.com/en/dev/internals/release-process/#term-Long-term-support-release[Django LTS^]
* PostgreSQL as database backend
* https://gunicorn.org/[Gunicorn^] Python WSGI HTTP Server
* https://caddyserver.com/[Caddy^] for serving static files and WSGI, or https://whitenoise.readthedocs.io/en/latest/[WhiteNoise^] (TBD)
* https://docs.astral.sh/uv/[Astral uv^] for Python project, dependency and build management
* https://htmx.org/[htmx] for the dynamic part in the frontend
* https://getbootstrap.com/[Bootstrap 5] for styling the frontend
A complete reasoning for this stack is available in https://vshnwiki.atlassian.net/wiki/spaces/VSHNPM/pages/402718747/Self-Service+Marketplace+Web+Application[our wiki^] (internal page).
== Development Paradigms
Keep usage of third-party dependencies low::
Every external dependency adds a burden on the maintenance of the application.
Adding a dependency must be done with care:
* Is the dependency well maintained and adopted in the ecosystem?
* Could we do it without the dependency? If not, why?
* What happens if the dependency is abandoned?
* Document the reason for each dependency, why it has been chosen and why we can't live without it.
Graceful degradation::
The application will connect to various upstream APIs which we can't control.
Should issues arise with one of these APIs, gracefully degrade the feature set and inform accordingly ("This service is currently not available - we're working on it").
Never must the application crash because and upstream API not being reachable, has slow response time or react in an undefined way.
Use database and caches wisely::
External systems like databases, caches or queues add additional complexity and burden to the application operations.
Every additional system must be chosen carefully and the reasoning documented.
Alternatives must be considered and documented.
Business logic vs. views::
Whenever possible, split business logic from views.
This allows to progress the application in the future to allow for different views (For example APIs or other alternative frontends).
Django specifics::
* We use class based views by default, exceptions can be made
* Dynamic configuration happens via environment variables
* Different environments (dev / test / prod) must be clearly separated inside the application
Testing::
Business functionality must be https://docs.djangoproject.com/en/5.1/topics/testing/[tested^] with code.
We preferrably use https://docs.pytest.org/[pytest^].
The source code can be found on the https://servala.app.codey.ch/servala/servala-portal[Servala Codey instance^].