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
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:
parent
fa3eb7c4fc
commit
c4f7c8df69
7 changed files with 860 additions and 56 deletions
|
@ -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="<mxfile><diagram id="iQWVNTns26_xaYTS_M6j" name="Page-1">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</diagram></mxfile>">
|
||||
<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: "Helvetica"; 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=""Helvetica"" 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: "Helvetica"; 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=""Helvetica"" 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: "Helvetica"; 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=""Helvetica"" 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: "Helvetica"; 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=""Helvetica"" 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: "Helvetica"; 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=""Helvetica"" 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: "Helvetica"; 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=""Helvetica"" 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: "Helvetica"; 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=""Helvetica"" 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: "Helvetica"; 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=""Helvetica"" 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: "Helvetica"; 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=""Helvetica"" 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: "Helvetica"; 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=""Helvetica"" 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: "Helvetica"; 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=""Helvetica"" 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: "Helvetica"; 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=""Helvetica"" 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: "Helvetica"; 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=""Helvetica"" 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: "Helvetica"; 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=""Helvetica"" 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: "Helvetica"; 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=""Helvetica"" 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: "Helvetica"; 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=""Helvetica"" 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: "Helvetica"; 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=""Helvetica"" 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: "Helvetica"; 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=""Helvetica"" 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 |
|
@ -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="<mxfile><diagram id="oCg05mIILSXJTHsOPe0H" name="Page-1">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==</diagram></mxfile>">
|
||||
<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: "Helvetica"; 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=""Helvetica"" 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: "Helvetica"; 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=""Helvetica"" 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: "Helvetica"; 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=""Helvetica"" 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: "Helvetica"; 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=""Helvetica"" 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: "Helvetica"; 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=""Helvetica"" 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: "Helvetica"; 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=""Helvetica"" 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: "Helvetica"; 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=""Helvetica"" 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: "Helvetica"; 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=""Helvetica"" 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: "Helvetica"; 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=""Helvetica"" 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 |
|
@ -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[]
|
59
docs/modules/ROOT/pages/web-portal-admin.adoc
Normal file
59
docs/modules/ROOT/pages/web-portal-admin.adoc
Normal 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.
|
30
docs/modules/ROOT/pages/web-portal-controlplanes.adoc
Normal file
30
docs/modules/ROOT/pages/web-portal-controlplanes.adoc
Normal 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
|
||||
----
|
64
docs/modules/ROOT/pages/web-portal-planning.adoc
Normal file
64
docs/modules/ROOT/pages/web-portal-planning.adoc
Normal 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^].
|
|
@ -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^].
|
Loading…
Add table
Add a link
Reference in a new issue