diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/404.html b/404.html new file mode 100644 index 00000000..6968aa00 --- /dev/null +++ b/404.html @@ -0,0 +1,440 @@ + + + + + + + + + + + + + + + + + + Cluster API driver for Magnum + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ +

404 - Not found

+ +
+
+ + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/SUMMARY/index.html b/SUMMARY/index.html new file mode 100644 index 00000000..206bf817 --- /dev/null +++ b/SUMMARY/index.html @@ -0,0 +1,464 @@ + + + + + + + + + + + + + + + + + + SUMMARY - Cluster API driver for Magnum + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

SUMMARY

+ +
    +
  • User Guide +
  • +
  • Administrator Guide
      +
    • admin/*.md
    • +
    +
  • +
  • Developer Guide
      +
    • developer/*.md
    • +
    +
  • +
+ + + + + + +
+
+ + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/admin/intro/index.html b/admin/intro/index.html new file mode 100644 index 00000000..8a6fca29 --- /dev/null +++ b/admin/intro/index.html @@ -0,0 +1,546 @@ + + + + + + + + + + + + + + + + + + + + + + Introduction - Cluster API driver for Magnum + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Introduction

+

The Cluster API driver for Magnum enables OpenStack Magnum, a container +orchestration service, to create and manage Kubernetes clusters using the +Cluster API framework. The Cluster API driver for Magnum leverages the power of +the Cluster API to simplify the deployment and management of Kubernetes clusters +within an OpenStack infrastructure.

+

With the Cluster API driver for Magnum, Magnum takes on the responsibility of +creating and maintaining the Cluster API resources. Magnum interacts with the +Cluster API controllers and reconcilers to dynamically provision and manage +Kubernetes clusters.

+

Magnum utilizes the capabilities of the Cluster API to define the desired state +of the Kubernetes clusters using familiar Cluster API resources such as Cluster, +MachineDeployment, and MachineSet. These resources encapsulate important +cluster configurations, including the number of control plane and worker nodes, +their specifications, and other relevant attributes.

+

By leveraging the Cluster API driver for Magnum, Magnum translates the desired +cluster specifications into Cluster API resources. It creates and manages these +resources, ensuring that the Kubernetes clusters are provisioned and maintained +according to the specified configurations.

+

Through this integration, Magnum empowers users to leverage the Cluster API's +declarative and consistent approach to manage their Kubernetes clusters in an +OpenStack environment. By leveraging Magnum's container orchestration +capabilities, users can easily create and scale Kubernetes deployments while +benefiting from the automation and extensibility provided by the Cluster API.

+

Reference

+

Here references some awesome Intro and Install blogs:

+ + + + + + + +
+
+ + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/admin/troubleshooting/index.html b/admin/troubleshooting/index.html new file mode 100644 index 00000000..f4c14224 --- /dev/null +++ b/admin/troubleshooting/index.html @@ -0,0 +1,730 @@ + + + + + + + + + + + + + + + + + + + + + + Troubleshooting - Cluster API driver for Magnum + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Troubleshooting

+

Useful commands

+

Tracking cluster progress using clusterctl

+

If you'd like to track the progress of a specific cluster from the clusterctl +perspective, you can run the following command to find out the stack_id of the +cluster and then use clusterctl describe to get the status of the cluster:

+
$ export CLUSTER_ID=$(openstack coe cluster show <cluster-name> -f value -c stack_id)
+$ watch -cn1 'clusterctl describe cluster -n magnum-system $CLUSTER_ID --grouping=false --color'
+
+

Cluster stuck in CREATE_IN_PROGRESS state

+

With the Cluster API driver for Magnum, the cluster creation process is +performed by the Cluster API for OpenStack. Due to the logic of how the +controller managers work, the process of creating a cluster is performed in +multiple steps and if a step fails, it will keep retrying until it succeeds.

+

Unlike the legacy Heat driver, the Cluster API driver for Magnum does not +move the state of the cluster to CREATE_FAILED if a step fails. Instead, +it will keep the cluster in CREATE_IN_PROGRESS state until the cluster is +successfully created or the cluster is deleted.

+

If you are experiencing issues with the cluster being stuck in CREATE_IN_PROGRESS +state, you can follow the steps below to troubleshoot the issue:

+
    +
  1. +

    Check the Cluster name from the stack_id field in Magnum:

    +
    $ openstack coe cluster show <cluster-name> -f value -c stack_id
    +
    +
  2. +
  3. +

    Check if the Cluster exists in the Kuberentes cluster using the stack_id:

    +
    $ kubectl -n magnum-system get clusters <stack-id>
    +
    +
    +

    Note

    +

    If the cluster exists and it is in Provisioned state, you can skip to +step 3.

    +
    +
  4. +
  5. +

    You will need to lookup the OpenStackCluster for the Cluster:

    +
    $ kubectl -n magnum-system get openstackclusters -l cluster.x-k8s.io/cluster-name=<stack-id>
    +
    +
    +

    Note

    +

    If the OpenStackCluster shows true for READY, you can skip to +step 4.

    +
    +
  6. +
  7. +

    You will have to look at the KubeadmControlPlane for + the OpenStackCluster:

    +
    $ kubectl -n magnum-system get kubeadmcontrolplanes -l cluster.x-k8s.io/cluster-name=<stack-id>
    +
    +
  8. +
  9. +

    If the number of READY nodes does not match the number of REPLICAS, + you will need to investigate if the instances are going up by looking at + the OpenStackMachine for the KubeadmControlPlane:

    +
    $ kubectl -n magnum-system describe openstackmachines -l cluster.x-k8s.io/control-plane=,cluster.x-k8s.io/cluster-name=<stack-id>
    +
    +

    From the output, you will need to look at the Status field and see if +any of the conditions are False. If they are, you will need to look at +the Message field to see what the error is.

    +
  10. +
+

Cluster stuck in DELETE_IN_PROGRESS state

+

Project deleted

+

If you have a case where a project has been deleted from OpenStack but the +cluster was not deleted, you will not be able to delete it even as an admin +user. You will find log messages such as the following which will indicate +that the project is missing:

+
E0705 17:11:00.333902       1 controller.go:326] "Reconciler error" err=<providerClient authentication err: Resource not found: [POST https://cloud.atmosphere.dev/v3/auth/tokens], error message: {"error":{"code":404,"message":"Could not find project: 1dfcc1f4399948baac7a83a6607f693c.","title":"Not Found"}}
+
+

In order to work around this issue, you will need to create a new project, +go into the database and update the id of the new project to match the +project_id of the cluster.

+
+

Warning

+

It is possible to corrupt the database if you do not know what you are +doing. Please make sure you have a backup of the database before

+
+
    +
  1. +

    Create a new project in OpenStack:

    +
    $ export NEW_PROJECT_ID=$(openstack project create cleanup-project -f value -c id)
    +
    +
  2. +
  3. +

    Get the existing project_id of the cluster:

    +
    $ export CURRENT_PROJECT_ID=$(openstack coe cluster show <cluster-name> -f value -c project_id)
    +
    +
  4. +
  5. +

    Update the id of the project in Keystone to match the project_id of + the cluster:

    +
    $ mysql -B -N -u root -p -e "update project set id='$CURRENT_PROJECT_ID' where id='$NEW_PROJECT_ID';" keystone
    +
    +

    If you're using Atmosphere, you can run the following:

    +
    $ kubectl -n openstack exec -it sts/percona-xtradb-pxc -- mysql -hlocalhost -uroot -p$(kubectl -n openstack get secret/percona-xtradb -ojson | jq -r '.data.root' | base64 --decode) -e "update project set id='$CURRENT_PROJECT_ID' where id='$NEW_PROJECT_ID';" keystone
    +
    +
  6. +
  7. +

    Verify that the project now exists under the new id:

    +
    $ openstack project show $CURRENT_PROJECT_ID
    +
    +
  8. +
  9. +

    Give access to your current admin user to the new project:

    +
    $ openstack role add --user $OS_USERNAME --project $CURRENT_PROJECT_ID member
    +
    +
  10. +
  11. +

    Switch to the context of that user

    +
    $ export OS_PROJECT_ID=$CURRENT_PROJECT_ID
    +
    +
  12. +
  13. +

    Create a new set of application credentials and update the existing + cloud-config secret for the cluster

    +
    $ export CAPI_CLUSTER_NAME=$(openstack coe cluster show tst1-useg-k8s-1 -f value -c stack_id)
    +$ export EXISTING_APPCRED_ID=$(kubectl -n magnum-system get secret/$CAPI_CLUSTER_NAME-cloud-config -ojson | jq -r '.data."clouds.yaml"' | base64 --decode | grep application_credential_id | awk '{print $2}')
    +$ export EXISTING_APPCRED_SECRET=$(kubectl -n magnum-system get secret/$CAPI_CLUSTER_NAME-cloud-config -ojson | jq -r '.data."clouds.yaml"' | base64 --decode | grep application_credential_secret | awk '{print $2}')
    +$ export NEW_APPCRED_ID=$(openstack application credential create --secret $EXISTING_APPCRED_SECRET $CAPI_CLUSTER_NAME-cleanup -f value -c id)
    +$  > /tmp/clouds.yaml
    +$ kubectl -n magnum-system patch secret/$CAPI_CLUSTER_NAME-cloud-config -p '{"data":{"clouds.yaml":"'$(kubectl -n magnum-system get secret/$CAPI_CLUSTER_NAME-cloud-config -ojson | jq -r '.data."clouds.yaml"' | base64 --decode | sed "s/$EXISTING_APPCRED_ID/$NEW_APPCRED_ID/" | base64 --wrap=0)'"}}'
    +
    +
  14. +
+

At this point, the cluster should start progressing on the deletion process, you +can verify this by running:

+
$ kubectl -n capo-system logs deploy/capo-controller-manager -f
+
+

Once the cluster is gone, you can clean up the project:

+
$ unset OS_PROJECT_ID
+$ openstack project delete $CURRENT_PROJECT_ID
+
+ + + + + + +
+
+ + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 00000000..1cf13b9f Binary files /dev/null and b/assets/images/favicon.png differ diff --git a/assets/javascripts/bundle.b4d07000.min.js b/assets/javascripts/bundle.b4d07000.min.js new file mode 100644 index 00000000..3c0bdad9 --- /dev/null +++ b/assets/javascripts/bundle.b4d07000.min.js @@ -0,0 +1,29 @@ +"use strict";(()=>{var Ci=Object.create;var gr=Object.defineProperty;var Ri=Object.getOwnPropertyDescriptor;var ki=Object.getOwnPropertyNames,Ht=Object.getOwnPropertySymbols,Hi=Object.getPrototypeOf,yr=Object.prototype.hasOwnProperty,nn=Object.prototype.propertyIsEnumerable;var rn=(e,t,r)=>t in e?gr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,P=(e,t)=>{for(var r in t||(t={}))yr.call(t,r)&&rn(e,r,t[r]);if(Ht)for(var r of Ht(t))nn.call(t,r)&&rn(e,r,t[r]);return e};var on=(e,t)=>{var r={};for(var n in e)yr.call(e,n)&&t.indexOf(n)<0&&(r[n]=e[n]);if(e!=null&&Ht)for(var n of Ht(e))t.indexOf(n)<0&&nn.call(e,n)&&(r[n]=e[n]);return r};var Pt=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Pi=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of ki(t))!yr.call(e,o)&&o!==r&&gr(e,o,{get:()=>t[o],enumerable:!(n=Ri(t,o))||n.enumerable});return e};var yt=(e,t,r)=>(r=e!=null?Ci(Hi(e)):{},Pi(t||!e||!e.__esModule?gr(r,"default",{value:e,enumerable:!0}):r,e));var sn=Pt((xr,an)=>{(function(e,t){typeof xr=="object"&&typeof an!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(xr,function(){"use strict";function e(r){var n=!0,o=!1,i=null,s={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function a(O){return!!(O&&O!==document&&O.nodeName!=="HTML"&&O.nodeName!=="BODY"&&"classList"in O&&"contains"in O.classList)}function f(O){var Qe=O.type,De=O.tagName;return!!(De==="INPUT"&&s[Qe]&&!O.readOnly||De==="TEXTAREA"&&!O.readOnly||O.isContentEditable)}function c(O){O.classList.contains("focus-visible")||(O.classList.add("focus-visible"),O.setAttribute("data-focus-visible-added",""))}function u(O){O.hasAttribute("data-focus-visible-added")&&(O.classList.remove("focus-visible"),O.removeAttribute("data-focus-visible-added"))}function p(O){O.metaKey||O.altKey||O.ctrlKey||(a(r.activeElement)&&c(r.activeElement),n=!0)}function m(O){n=!1}function d(O){a(O.target)&&(n||f(O.target))&&c(O.target)}function h(O){a(O.target)&&(O.target.classList.contains("focus-visible")||O.target.hasAttribute("data-focus-visible-added"))&&(o=!0,window.clearTimeout(i),i=window.setTimeout(function(){o=!1},100),u(O.target))}function v(O){document.visibilityState==="hidden"&&(o&&(n=!0),Y())}function Y(){document.addEventListener("mousemove",N),document.addEventListener("mousedown",N),document.addEventListener("mouseup",N),document.addEventListener("pointermove",N),document.addEventListener("pointerdown",N),document.addEventListener("pointerup",N),document.addEventListener("touchmove",N),document.addEventListener("touchstart",N),document.addEventListener("touchend",N)}function B(){document.removeEventListener("mousemove",N),document.removeEventListener("mousedown",N),document.removeEventListener("mouseup",N),document.removeEventListener("pointermove",N),document.removeEventListener("pointerdown",N),document.removeEventListener("pointerup",N),document.removeEventListener("touchmove",N),document.removeEventListener("touchstart",N),document.removeEventListener("touchend",N)}function N(O){O.target.nodeName&&O.target.nodeName.toLowerCase()==="html"||(n=!1,B())}document.addEventListener("keydown",p,!0),document.addEventListener("mousedown",m,!0),document.addEventListener("pointerdown",m,!0),document.addEventListener("touchstart",m,!0),document.addEventListener("visibilitychange",v,!0),Y(),r.addEventListener("focus",d,!0),r.addEventListener("blur",h,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var cn=Pt(Er=>{(function(e){var t=function(){try{return!!Symbol.iterator}catch(c){return!1}},r=t(),n=function(c){var u={next:function(){var p=c.shift();return{done:p===void 0,value:p}}};return r&&(u[Symbol.iterator]=function(){return u}),u},o=function(c){return encodeURIComponent(c).replace(/%20/g,"+")},i=function(c){return decodeURIComponent(String(c).replace(/\+/g," "))},s=function(){var c=function(p){Object.defineProperty(this,"_entries",{writable:!0,value:{}});var m=typeof p;if(m!=="undefined")if(m==="string")p!==""&&this._fromString(p);else if(p instanceof c){var d=this;p.forEach(function(B,N){d.append(N,B)})}else if(p!==null&&m==="object")if(Object.prototype.toString.call(p)==="[object Array]")for(var h=0;hd[0]?1:0}),c._entries&&(c._entries={});for(var p=0;p1?i(d[1]):"")}})})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Er);(function(e){var t=function(){try{var o=new e.URL("b","http://a");return o.pathname="c d",o.href==="http://a/c%20d"&&o.searchParams}catch(i){return!1}},r=function(){var o=e.URL,i=function(f,c){typeof f!="string"&&(f=String(f)),c&&typeof c!="string"&&(c=String(c));var u=document,p;if(c&&(e.location===void 0||c!==e.location.href)){c=c.toLowerCase(),u=document.implementation.createHTMLDocument(""),p=u.createElement("base"),p.href=c,u.head.appendChild(p);try{if(p.href.indexOf(c)!==0)throw new Error(p.href)}catch(O){throw new Error("URL unable to set base "+c+" due to "+O)}}var m=u.createElement("a");m.href=f,p&&(u.body.appendChild(m),m.href=m.href);var d=u.createElement("input");if(d.type="url",d.value=f,m.protocol===":"||!/:/.test(m.href)||!d.checkValidity()&&!c)throw new TypeError("Invalid URL");Object.defineProperty(this,"_anchorElement",{value:m});var h=new e.URLSearchParams(this.search),v=!0,Y=!0,B=this;["append","delete","set"].forEach(function(O){var Qe=h[O];h[O]=function(){Qe.apply(h,arguments),v&&(Y=!1,B.search=h.toString(),Y=!0)}}),Object.defineProperty(this,"searchParams",{value:h,enumerable:!0});var N=void 0;Object.defineProperty(this,"_updateSearchParams",{enumerable:!1,configurable:!1,writable:!1,value:function(){this.search!==N&&(N=this.search,Y&&(v=!1,this.searchParams._fromString(this.search),v=!0))}})},s=i.prototype,a=function(f){Object.defineProperty(s,f,{get:function(){return this._anchorElement[f]},set:function(c){this._anchorElement[f]=c},enumerable:!0})};["hash","host","hostname","port","protocol"].forEach(function(f){a(f)}),Object.defineProperty(s,"search",{get:function(){return this._anchorElement.search},set:function(f){this._anchorElement.search=f,this._updateSearchParams()},enumerable:!0}),Object.defineProperties(s,{toString:{get:function(){var f=this;return function(){return f.href}}},href:{get:function(){return this._anchorElement.href.replace(/\?$/,"")},set:function(f){this._anchorElement.href=f,this._updateSearchParams()},enumerable:!0},pathname:{get:function(){return this._anchorElement.pathname.replace(/(^\/?)/,"/")},set:function(f){this._anchorElement.pathname=f},enumerable:!0},origin:{get:function(){var f={"http:":80,"https:":443,"ftp:":21}[this._anchorElement.protocol],c=this._anchorElement.port!=f&&this._anchorElement.port!=="";return this._anchorElement.protocol+"//"+this._anchorElement.hostname+(c?":"+this._anchorElement.port:"")},enumerable:!0},password:{get:function(){return""},set:function(f){},enumerable:!0},username:{get:function(){return""},set:function(f){},enumerable:!0}}),i.createObjectURL=function(f){return o.createObjectURL.apply(o,arguments)},i.revokeObjectURL=function(f){return o.revokeObjectURL.apply(o,arguments)},e.URL=i};if(t()||r(),e.location!==void 0&&!("origin"in e.location)){var n=function(){return e.location.protocol+"//"+e.location.hostname+(e.location.port?":"+e.location.port:"")};try{Object.defineProperty(e.location,"origin",{get:n,enumerable:!0})}catch(o){setInterval(function(){e.location.origin=n()},100)}}})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Er)});var qr=Pt((Mt,Nr)=>{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof Mt=="object"&&typeof Nr=="object"?Nr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Mt=="object"?Mt.ClipboardJS=r():t.ClipboardJS=r()})(Mt,function(){return function(){var e={686:function(n,o,i){"use strict";i.d(o,{default:function(){return Ai}});var s=i(279),a=i.n(s),f=i(370),c=i.n(f),u=i(817),p=i.n(u);function m(j){try{return document.execCommand(j)}catch(T){return!1}}var d=function(T){var E=p()(T);return m("cut"),E},h=d;function v(j){var T=document.documentElement.getAttribute("dir")==="rtl",E=document.createElement("textarea");E.style.fontSize="12pt",E.style.border="0",E.style.padding="0",E.style.margin="0",E.style.position="absolute",E.style[T?"right":"left"]="-9999px";var H=window.pageYOffset||document.documentElement.scrollTop;return E.style.top="".concat(H,"px"),E.setAttribute("readonly",""),E.value=j,E}var Y=function(T,E){var H=v(T);E.container.appendChild(H);var I=p()(H);return m("copy"),H.remove(),I},B=function(T){var E=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},H="";return typeof T=="string"?H=Y(T,E):T instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(T==null?void 0:T.type)?H=Y(T.value,E):(H=p()(T),m("copy")),H},N=B;function O(j){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?O=function(E){return typeof E}:O=function(E){return E&&typeof Symbol=="function"&&E.constructor===Symbol&&E!==Symbol.prototype?"symbol":typeof E},O(j)}var Qe=function(){var T=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},E=T.action,H=E===void 0?"copy":E,I=T.container,q=T.target,Me=T.text;if(H!=="copy"&&H!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(q!==void 0)if(q&&O(q)==="object"&&q.nodeType===1){if(H==="copy"&&q.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(H==="cut"&&(q.hasAttribute("readonly")||q.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(Me)return N(Me,{container:I});if(q)return H==="cut"?h(q):N(q,{container:I})},De=Qe;function $e(j){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?$e=function(E){return typeof E}:$e=function(E){return E&&typeof Symbol=="function"&&E.constructor===Symbol&&E!==Symbol.prototype?"symbol":typeof E},$e(j)}function Ei(j,T){if(!(j instanceof T))throw new TypeError("Cannot call a class as a function")}function tn(j,T){for(var E=0;E0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof I.action=="function"?I.action:this.defaultAction,this.target=typeof I.target=="function"?I.target:this.defaultTarget,this.text=typeof I.text=="function"?I.text:this.defaultText,this.container=$e(I.container)==="object"?I.container:document.body}},{key:"listenClick",value:function(I){var q=this;this.listener=c()(I,"click",function(Me){return q.onClick(Me)})}},{key:"onClick",value:function(I){var q=I.delegateTarget||I.currentTarget,Me=this.action(q)||"copy",kt=De({action:Me,container:this.container,target:this.target(q),text:this.text(q)});this.emit(kt?"success":"error",{action:Me,text:kt,trigger:q,clearSelection:function(){q&&q.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(I){return vr("action",I)}},{key:"defaultTarget",value:function(I){var q=vr("target",I);if(q)return document.querySelector(q)}},{key:"defaultText",value:function(I){return vr("text",I)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(I){var q=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return N(I,q)}},{key:"cut",value:function(I){return h(I)}},{key:"isSupported",value:function(){var I=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],q=typeof I=="string"?[I]:I,Me=!!document.queryCommandSupported;return q.forEach(function(kt){Me=Me&&!!document.queryCommandSupported(kt)}),Me}}]),E}(a()),Ai=Li},828:function(n){var o=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function s(a,f){for(;a&&a.nodeType!==o;){if(typeof a.matches=="function"&&a.matches(f))return a;a=a.parentNode}}n.exports=s},438:function(n,o,i){var s=i(828);function a(u,p,m,d,h){var v=c.apply(this,arguments);return u.addEventListener(m,v,h),{destroy:function(){u.removeEventListener(m,v,h)}}}function f(u,p,m,d,h){return typeof u.addEventListener=="function"?a.apply(null,arguments):typeof m=="function"?a.bind(null,document).apply(null,arguments):(typeof u=="string"&&(u=document.querySelectorAll(u)),Array.prototype.map.call(u,function(v){return a(v,p,m,d,h)}))}function c(u,p,m,d){return function(h){h.delegateTarget=s(h.target,p),h.delegateTarget&&d.call(u,h)}}n.exports=f},879:function(n,o){o.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},o.nodeList=function(i){var s=Object.prototype.toString.call(i);return i!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in i&&(i.length===0||o.node(i[0]))},o.string=function(i){return typeof i=="string"||i instanceof String},o.fn=function(i){var s=Object.prototype.toString.call(i);return s==="[object Function]"}},370:function(n,o,i){var s=i(879),a=i(438);function f(m,d,h){if(!m&&!d&&!h)throw new Error("Missing required arguments");if(!s.string(d))throw new TypeError("Second argument must be a String");if(!s.fn(h))throw new TypeError("Third argument must be a Function");if(s.node(m))return c(m,d,h);if(s.nodeList(m))return u(m,d,h);if(s.string(m))return p(m,d,h);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function c(m,d,h){return m.addEventListener(d,h),{destroy:function(){m.removeEventListener(d,h)}}}function u(m,d,h){return Array.prototype.forEach.call(m,function(v){v.addEventListener(d,h)}),{destroy:function(){Array.prototype.forEach.call(m,function(v){v.removeEventListener(d,h)})}}}function p(m,d,h){return a(document.body,m,d,h)}n.exports=f},817:function(n){function o(i){var s;if(i.nodeName==="SELECT")i.focus(),s=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var a=i.hasAttribute("readonly");a||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),a||i.removeAttribute("readonly"),s=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var f=window.getSelection(),c=document.createRange();c.selectNodeContents(i),f.removeAllRanges(),f.addRange(c),s=f.toString()}return s}n.exports=o},279:function(n){function o(){}o.prototype={on:function(i,s,a){var f=this.e||(this.e={});return(f[i]||(f[i]=[])).push({fn:s,ctx:a}),this},once:function(i,s,a){var f=this;function c(){f.off(i,c),s.apply(a,arguments)}return c._=s,this.on(i,c,a)},emit:function(i){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[i]||[]).slice(),f=0,c=a.length;for(f;f{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var rs=/["'&<>]/;Yo.exports=ns;function ns(e){var t=""+e,r=rs.exec(t);if(!r)return t;var n,o="",i=0,s=0;for(i=r.index;i0&&i[i.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!i||c[1]>i[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function W(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),o,i=[],s;try{for(;(t===void 0||t-- >0)&&!(o=n.next()).done;)i.push(o.value)}catch(a){s={error:a}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return i}function D(e,t,r){if(r||arguments.length===2)for(var n=0,o=t.length,i;n1||a(m,d)})})}function a(m,d){try{f(n[m](d))}catch(h){p(i[0][3],h)}}function f(m){m.value instanceof et?Promise.resolve(m.value.v).then(c,u):p(i[0][2],m)}function c(m){a("next",m)}function u(m){a("throw",m)}function p(m,d){m(d),i.shift(),i.length&&a(i[0][0],i[0][1])}}function pn(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof Ee=="function"?Ee(e):e[Symbol.iterator](),r={},n("next"),n("throw"),n("return"),r[Symbol.asyncIterator]=function(){return this},r);function n(i){r[i]=e[i]&&function(s){return new Promise(function(a,f){s=e[i](s),o(a,f,s.done,s.value)})}}function o(i,s,a,f){Promise.resolve(f).then(function(c){i({value:c,done:a})},s)}}function C(e){return typeof e=="function"}function at(e){var t=function(n){Error.call(n),n.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var It=at(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(n,o){return o+1+") "+n.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function Ve(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Ie=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,n,o,i;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=Ee(s),f=a.next();!f.done;f=a.next()){var c=f.value;c.remove(this)}}catch(v){t={error:v}}finally{try{f&&!f.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}else s.remove(this);var u=this.initialTeardown;if(C(u))try{u()}catch(v){i=v instanceof It?v.errors:[v]}var p=this._finalizers;if(p){this._finalizers=null;try{for(var m=Ee(p),d=m.next();!d.done;d=m.next()){var h=d.value;try{ln(h)}catch(v){i=i!=null?i:[],v instanceof It?i=D(D([],W(i)),W(v.errors)):i.push(v)}}}catch(v){n={error:v}}finally{try{d&&!d.done&&(o=m.return)&&o.call(m)}finally{if(n)throw n.error}}}if(i)throw new It(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)ln(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Ve(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Ve(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Sr=Ie.EMPTY;function jt(e){return e instanceof Ie||e&&"closed"in e&&C(e.remove)&&C(e.add)&&C(e.unsubscribe)}function ln(e){C(e)?e():e.unsubscribe()}var Le={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var st={setTimeout:function(e,t){for(var r=[],n=2;n0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var n=this,o=this,i=o.hasError,s=o.isStopped,a=o.observers;return i||s?Sr:(this.currentObservers=null,a.push(r),new Ie(function(){n.currentObservers=null,Ve(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var n=this,o=n.hasError,i=n.thrownError,s=n.isStopped;o?r.error(i):s&&r.complete()},t.prototype.asObservable=function(){var r=new F;return r.source=this,r},t.create=function(r,n){return new xn(r,n)},t}(F);var xn=function(e){ie(t,e);function t(r,n){var o=e.call(this)||this;return o.destination=r,o.source=n,o}return t.prototype.next=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.next)===null||o===void 0||o.call(n,r)},t.prototype.error=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.error)===null||o===void 0||o.call(n,r)},t.prototype.complete=function(){var r,n;(n=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||n===void 0||n.call(r)},t.prototype._subscribe=function(r){var n,o;return(o=(n=this.source)===null||n===void 0?void 0:n.subscribe(r))!==null&&o!==void 0?o:Sr},t}(x);var Et={now:function(){return(Et.delegate||Date).now()},delegate:void 0};var wt=function(e){ie(t,e);function t(r,n,o){r===void 0&&(r=1/0),n===void 0&&(n=1/0),o===void 0&&(o=Et);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=n,i._timestampProvider=o,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=n===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,n),i}return t.prototype.next=function(r){var n=this,o=n.isStopped,i=n._buffer,s=n._infiniteTimeWindow,a=n._timestampProvider,f=n._windowTime;o||(i.push(r),!s&&i.push(a.now()+f)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var n=this._innerSubscribe(r),o=this,i=o._infiniteTimeWindow,s=o._buffer,a=s.slice(),f=0;f0?e.prototype.requestAsyncId.call(this,r,n,o):(r.actions.push(this),r._scheduled||(r._scheduled=ut.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,n,o){var i;if(o===void 0&&(o=0),o!=null?o>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,n,o);var s=r.actions;n!=null&&((i=s[s.length-1])===null||i===void 0?void 0:i.id)!==n&&(ut.cancelAnimationFrame(n),r._scheduled=void 0)},t}(Wt);var Sn=function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var n=this._scheduled;this._scheduled=void 0;var o=this.actions,i;r=r||o.shift();do if(i=r.execute(r.state,r.delay))break;while((r=o[0])&&r.id===n&&o.shift());if(this._active=!1,i){for(;(r=o[0])&&r.id===n&&o.shift();)r.unsubscribe();throw i}},t}(Dt);var Oe=new Sn(wn);var _=new F(function(e){return e.complete()});function Vt(e){return e&&C(e.schedule)}function Cr(e){return e[e.length-1]}function Ye(e){return C(Cr(e))?e.pop():void 0}function Te(e){return Vt(Cr(e))?e.pop():void 0}function zt(e,t){return typeof Cr(e)=="number"?e.pop():t}var pt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function Nt(e){return C(e==null?void 0:e.then)}function qt(e){return C(e[ft])}function Kt(e){return Symbol.asyncIterator&&C(e==null?void 0:e[Symbol.asyncIterator])}function Qt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function zi(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var Yt=zi();function Gt(e){return C(e==null?void 0:e[Yt])}function Bt(e){return un(this,arguments,function(){var r,n,o,i;return $t(this,function(s){switch(s.label){case 0:r=e.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,et(r.read())];case 3:return n=s.sent(),o=n.value,i=n.done,i?[4,et(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,et(o)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function Jt(e){return C(e==null?void 0:e.getReader)}function U(e){if(e instanceof F)return e;if(e!=null){if(qt(e))return Ni(e);if(pt(e))return qi(e);if(Nt(e))return Ki(e);if(Kt(e))return On(e);if(Gt(e))return Qi(e);if(Jt(e))return Yi(e)}throw Qt(e)}function Ni(e){return new F(function(t){var r=e[ft]();if(C(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function qi(e){return new F(function(t){for(var r=0;r=2;return function(n){return n.pipe(e?A(function(o,i){return e(o,i,n)}):de,ge(1),r?He(t):Dn(function(){return new Zt}))}}function Vn(){for(var e=[],t=0;t=2,!0))}function pe(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new x}:t,n=e.resetOnError,o=n===void 0?!0:n,i=e.resetOnComplete,s=i===void 0?!0:i,a=e.resetOnRefCountZero,f=a===void 0?!0:a;return function(c){var u,p,m,d=0,h=!1,v=!1,Y=function(){p==null||p.unsubscribe(),p=void 0},B=function(){Y(),u=m=void 0,h=v=!1},N=function(){var O=u;B(),O==null||O.unsubscribe()};return y(function(O,Qe){d++,!v&&!h&&Y();var De=m=m!=null?m:r();Qe.add(function(){d--,d===0&&!v&&!h&&(p=$r(N,f))}),De.subscribe(Qe),!u&&d>0&&(u=new rt({next:function($e){return De.next($e)},error:function($e){v=!0,Y(),p=$r(B,o,$e),De.error($e)},complete:function(){h=!0,Y(),p=$r(B,s),De.complete()}}),U(O).subscribe(u))})(c)}}function $r(e,t){for(var r=[],n=2;ne.next(document)),e}function K(e,t=document){return Array.from(t.querySelectorAll(e))}function z(e,t=document){let r=ce(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function ce(e,t=document){return t.querySelector(e)||void 0}function _e(){return document.activeElement instanceof HTMLElement&&document.activeElement||void 0}function tr(e){return L(b(document.body,"focusin"),b(document.body,"focusout")).pipe(ke(1),l(()=>{let t=_e();return typeof t!="undefined"?e.contains(t):!1}),V(e===_e()),J())}function Xe(e){return{x:e.offsetLeft,y:e.offsetTop}}function Kn(e){return L(b(window,"load"),b(window,"resize")).pipe(Ce(0,Oe),l(()=>Xe(e)),V(Xe(e)))}function rr(e){return{x:e.scrollLeft,y:e.scrollTop}}function dt(e){return L(b(e,"scroll"),b(window,"resize")).pipe(Ce(0,Oe),l(()=>rr(e)),V(rr(e)))}var Yn=function(){if(typeof Map!="undefined")return Map;function e(t,r){var n=-1;return t.some(function(o,i){return o[0]===r?(n=i,!0):!1}),n}return function(){function t(){this.__entries__=[]}return Object.defineProperty(t.prototype,"size",{get:function(){return this.__entries__.length},enumerable:!0,configurable:!0}),t.prototype.get=function(r){var n=e(this.__entries__,r),o=this.__entries__[n];return o&&o[1]},t.prototype.set=function(r,n){var o=e(this.__entries__,r);~o?this.__entries__[o][1]=n:this.__entries__.push([r,n])},t.prototype.delete=function(r){var n=this.__entries__,o=e(n,r);~o&&n.splice(o,1)},t.prototype.has=function(r){return!!~e(this.__entries__,r)},t.prototype.clear=function(){this.__entries__.splice(0)},t.prototype.forEach=function(r,n){n===void 0&&(n=null);for(var o=0,i=this.__entries__;o0},e.prototype.connect_=function(){!Wr||this.connected_||(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),va?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},e.prototype.disconnect_=function(){!Wr||!this.connected_||(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},e.prototype.onTransitionEnd_=function(t){var r=t.propertyName,n=r===void 0?"":r,o=ba.some(function(i){return!!~n.indexOf(i)});o&&this.refresh()},e.getInstance=function(){return this.instance_||(this.instance_=new e),this.instance_},e.instance_=null,e}(),Gn=function(e,t){for(var r=0,n=Object.keys(t);r0},e}(),Jn=typeof WeakMap!="undefined"?new WeakMap:new Yn,Xn=function(){function e(t){if(!(this instanceof e))throw new TypeError("Cannot call a class as a function.");if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");var r=ga.getInstance(),n=new La(t,r,this);Jn.set(this,n)}return e}();["observe","unobserve","disconnect"].forEach(function(e){Xn.prototype[e]=function(){var t;return(t=Jn.get(this))[e].apply(t,arguments)}});var Aa=function(){return typeof nr.ResizeObserver!="undefined"?nr.ResizeObserver:Xn}(),Zn=Aa;var eo=new x,Ca=$(()=>k(new Zn(e=>{for(let t of e)eo.next(t)}))).pipe(g(e=>L(ze,k(e)).pipe(R(()=>e.disconnect()))),X(1));function he(e){return{width:e.offsetWidth,height:e.offsetHeight}}function ye(e){return Ca.pipe(S(t=>t.observe(e)),g(t=>eo.pipe(A(({target:r})=>r===e),R(()=>t.unobserve(e)),l(()=>he(e)))),V(he(e)))}function bt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function ar(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}var to=new x,Ra=$(()=>k(new IntersectionObserver(e=>{for(let t of e)to.next(t)},{threshold:0}))).pipe(g(e=>L(ze,k(e)).pipe(R(()=>e.disconnect()))),X(1));function sr(e){return Ra.pipe(S(t=>t.observe(e)),g(t=>to.pipe(A(({target:r})=>r===e),R(()=>t.unobserve(e)),l(({isIntersecting:r})=>r))))}function ro(e,t=16){return dt(e).pipe(l(({y:r})=>{let n=he(e),o=bt(e);return r>=o.height-n.height-t}),J())}var cr={drawer:z("[data-md-toggle=drawer]"),search:z("[data-md-toggle=search]")};function no(e){return cr[e].checked}function Ke(e,t){cr[e].checked!==t&&cr[e].click()}function Ue(e){let t=cr[e];return b(t,"change").pipe(l(()=>t.checked),V(t.checked))}function ka(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Ha(){return L(b(window,"compositionstart").pipe(l(()=>!0)),b(window,"compositionend").pipe(l(()=>!1))).pipe(V(!1))}function oo(){let e=b(window,"keydown").pipe(A(t=>!(t.metaKey||t.ctrlKey)),l(t=>({mode:no("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),A(({mode:t,type:r})=>{if(t==="global"){let n=_e();if(typeof n!="undefined")return!ka(n,r)}return!0}),pe());return Ha().pipe(g(t=>t?_:e))}function le(){return new URL(location.href)}function ot(e){location.href=e.href}function io(){return new x}function ao(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)ao(e,r)}function M(e,t,...r){let n=document.createElement(e);if(t)for(let o of Object.keys(t))typeof t[o]!="undefined"&&(typeof t[o]!="boolean"?n.setAttribute(o,t[o]):n.setAttribute(o,""));for(let o of r)ao(n,o);return n}function fr(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function so(){return location.hash.substring(1)}function Dr(e){let t=M("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Pa(e){return L(b(window,"hashchange"),e).pipe(l(so),V(so()),A(t=>t.length>0),X(1))}function co(e){return Pa(e).pipe(l(t=>ce(`[id="${t}"]`)),A(t=>typeof t!="undefined"))}function Vr(e){let t=matchMedia(e);return er(r=>t.addListener(()=>r(t.matches))).pipe(V(t.matches))}function fo(){let e=matchMedia("print");return L(b(window,"beforeprint").pipe(l(()=>!0)),b(window,"afterprint").pipe(l(()=>!1))).pipe(V(e.matches))}function zr(e,t){return e.pipe(g(r=>r?t():_))}function ur(e,t={credentials:"same-origin"}){return ue(fetch(`${e}`,t)).pipe(fe(()=>_),g(r=>r.status!==200?Ot(()=>new Error(r.statusText)):k(r)))}function We(e,t){return ur(e,t).pipe(g(r=>r.json()),X(1))}function uo(e,t){let r=new DOMParser;return ur(e,t).pipe(g(n=>n.text()),l(n=>r.parseFromString(n,"text/xml")),X(1))}function pr(e){let t=M("script",{src:e});return $(()=>(document.head.appendChild(t),L(b(t,"load"),b(t,"error").pipe(g(()=>Ot(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(l(()=>{}),R(()=>document.head.removeChild(t)),ge(1))))}function po(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function lo(){return L(b(window,"scroll",{passive:!0}),b(window,"resize",{passive:!0})).pipe(l(po),V(po()))}function mo(){return{width:innerWidth,height:innerHeight}}function ho(){return b(window,"resize",{passive:!0}).pipe(l(mo),V(mo()))}function bo(){return G([lo(),ho()]).pipe(l(([e,t])=>({offset:e,size:t})),X(1))}function lr(e,{viewport$:t,header$:r}){let n=t.pipe(ee("size")),o=G([n,r]).pipe(l(()=>Xe(e)));return G([r,t,o]).pipe(l(([{height:i},{offset:s,size:a},{x:f,y:c}])=>({offset:{x:s.x-f,y:s.y-c+i},size:a})))}(()=>{function e(n,o){parent.postMessage(n,o||"*")}function t(...n){return n.reduce((o,i)=>o.then(()=>new Promise(s=>{let a=document.createElement("script");a.src=i,a.onload=s,document.body.appendChild(a)})),Promise.resolve())}var r=class extends EventTarget{constructor(n){super(),this.url=n,this.m=i=>{i.source===this.w&&(this.dispatchEvent(new MessageEvent("message",{data:i.data})),this.onmessage&&this.onmessage(i))},this.e=(i,s,a,f,c)=>{if(s===`${this.url}`){let u=new ErrorEvent("error",{message:i,filename:s,lineno:a,colno:f,error:c});this.dispatchEvent(u),this.onerror&&this.onerror(u)}};let o=document.createElement("iframe");o.hidden=!0,document.body.appendChild(this.iframe=o),this.w.document.open(),this.w.document.write(` + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Cluster Topology

+

The Cluster API driver for Magnum makes use of the Cluster topology feature of the Cluster API project. This allows it to delegate all of the work around building resources such as the OpenStackCluster, MachineDeployments and everything else managed entire by the Cluster API instead of the driver creating all of these resources.

+

In order to do this, the driver creates a ClusterClass resource which is called magnum-v{VERSION} where {VERSION} is the current version of the driver because of the following reasons:

+
    +
  • This allows us to have multiple different versions of the ClusterClass because it is an immutable resource.
  • +
  • This prevents causing a rollout of existing clusters should a change happen to the underlying ClusterClass.
  • +
+

It's important to note that there are only one scenarios where the spec.topology.class for a given Cluster will be modified and this will be when a cluster upgrade is done. This is because there is an expectation by the user that a rolling restart operation will occur if a cluster upgrade is requested. No other action should be allowed to change the spec.topology.class of a Cluster.

+

For users, it's important to keep in mind that if they want to use a newer ClusterClass in order to make sure of a new feature available in a newer ClusterClass, they can simply do an upgrade within Magnum to the same cluster template and it will actually force an update of the spec.topology.class, which might then naturally cause a full rollout to occur.

+ + + + + + +
+
+ + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/developer/testing-and-development/index.html b/developer/testing-and-development/index.html new file mode 100644 index 00000000..f7fb6fa8 --- /dev/null +++ b/developer/testing-and-development/index.html @@ -0,0 +1,518 @@ + + + + + + + + + + + + + + + + + + + + + + Testing & Development - Cluster API driver for Magnum + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Testing & Development

+

In order to be able to test and develop the magnum-cluster-api project, you +will need to have an existing Magnum deployment. You can use the following +steps to be able to test and develop the project.

+
    +
  1. Start up a DevStack environment with all Cluster API dependencies
  2. +
+
./hack/stack.sh
+
+
    +
  1. Upload an image to use with Magnum and create cluster templates
  2. +
+
pushd /tmp
+source /opt/stack/openrc
+export OS_DISTRO=ubuntu # you can change this to "flatcar" if you want to use Flatcar
+for version in v1.24.16 v1.25.12 v1.26.7 v1.27.4; do \
+   [[ "${OS_DISTRO}" == "ubuntu" ]] && IMAGE_NAME="ubuntu-2204-kube-${version}" || IMAGE_NAME="flatcar-kube-${version}"; \
+   curl -LO https://object-storage.public.mtl1.vexxhost.net/swift/v1/a91f106f55e64246babde7402c21b87a/magnum-capi/${IMAGE_NAME}.qcow2; \
+   openstack image create ${IMAGE_NAME} --disk-format=qcow2 --container-format=bare --property os_distro=${OS_DISTRO} --file=${IMAGE_NAME}.qcow2; \
+   openstack coe cluster template create \
+     --image $(openstack image show ${IMAGE_NAME} -c id -f value) \
+     --external-network public \
+     --dns-nameserver 8.8.8.8 \
+     --master-lb-enabled \
+     --master-flavor m1.medium \
+     --flavor m1.medium \
+     --network-driver calico \
+     --docker-storage-driver overlay2 \
+     --coe kubernetes \
+     --label kube_tag=${version} \
+     k8s-${version};
+done;
+popd
+
+
    +
  1. Spin up a new cluster using the Cluster API driver
  2. +
+
openstack coe cluster create \
+  --cluster-template k8s-v1.25.12 \
+  --master-count 3 \
+  --node-count 2 \
+  k8s-v1.25.12
+
+
    +
  1. Once the cluster reaches CREATE_COMPLETE state, you can interact with it:
  2. +
+
eval $(openstack coe cluster config k8s-v1.25.12)
+
+ + + + + + +
+
+ + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/developer/upgrade-in-atmosphere/index.html b/developer/upgrade-in-atmosphere/index.html new file mode 100644 index 00000000..2c99f11c --- /dev/null +++ b/developer/upgrade-in-atmosphere/index.html @@ -0,0 +1,549 @@ + + + + + + + + + + + + + + + + + + + + Upgrading in Atmosphere environment - Cluster API driver for Magnum + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Upgrading in Atmosphere environment

+

In Atmosphere environment, Magnum cluster-api is embedded in the Magnum conductor container. +Therefore, to upgrade Magnum cluster-api in your atmosphere environment, developers have to build a new Magnum container image with the desired revision. +On the other hand, magnum-cluster-api repository has a Github workflow to build and push Magnum container images which include the latest Magnum cluster-api driver code. So developers can trigger this workflow to get new Magnum images with the latest code.

+

To apply a new release

+

Once a new release is published, image build workflow is triggered automatically and new container images is published to quay.io/vexxhost/magnum-cluster-api. So there is no need to run images again and just need to upgrade the image ref in Atmosphere deployment code. +Run the following cmd to update all Magnum image tags in Atmosphere project. +

earthly +pin-images
+
+It will fetch the hash value of the current head image and set it in roles/default/vars/main.yaml file. Then you can run ansible-playbook or poetry command to deploy/upgrade atmosphere as normal.

+

To apply main branch (not released yet)

+

If you want to apply patches merged into main branch but not released yet, you can follow this instruction. +- First, build new Magnum container images by running image workflow with Push images to Container Registry enabled at https://github.com/vexxhost/magnum-cluster-api/actions/workflows/image.yml. +- Once the workflow is successfully finished, new images will be pushed to quay.io/vexxhost/magnum-cluster-api. You can get the exact image tag hash value from the workflow log. (note: It will not promote images so need to get the exact image digest hash value.) +- Update the image tags in roles/default/vars/main.yaml of atmosphere project. https://github.com/vexxhost/atmosphere/blob/c7c0de94112448522abb8973483da82eb5f937a8/roles/defaults/vars/main.yml#L101-L105 +- Run atmosphere playbook again.

+

Or you can just update the images on-fly using kubectl CLI in your Atmosphere environment once you know the image ref but this is not recommended. +

kubectl set image sts/magnum-conductor magnum-conductor=${IMAGE_REF} magnum-conductor-init=${IMAGE_REF} -n openstack
+kubectl set image deploy/magnum-api magnum-api=${IMAGE_REF} -n openstack
+kubectl set image deploy/magnum-registry registry=${IMAGE_REF} -n openstack
+

+ + + + + + +
+
+ + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 00000000..13fbb073 --- /dev/null +++ b/index.html @@ -0,0 +1,450 @@ + + + + + + + + + + + + + + + + + + Cluster API driver for Magnum + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Home

+ + + + + + + + +
+
+ + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 00000000..0f8724ef --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz new file mode 100644 index 00000000..9b3d57bd Binary files /dev/null and b/sitemap.xml.gz differ diff --git a/static/logo.png b/static/logo.png new file mode 100644 index 00000000..7d9632f9 Binary files /dev/null and b/static/logo.png differ diff --git a/static/user/getting-started/cluster-create-info.png b/static/user/getting-started/cluster-create-info.png new file mode 100644 index 00000000..2e000728 Binary files /dev/null and b/static/user/getting-started/cluster-create-info.png differ diff --git a/static/user/getting-started/cluster-create-mgmt.png b/static/user/getting-started/cluster-create-mgmt.png new file mode 100644 index 00000000..2ccbdf93 Binary files /dev/null and b/static/user/getting-started/cluster-create-mgmt.png differ diff --git a/static/user/getting-started/cluster-create-network.png b/static/user/getting-started/cluster-create-network.png new file mode 100644 index 00000000..becc7a29 Binary files /dev/null and b/static/user/getting-started/cluster-create-network.png differ diff --git a/static/user/getting-started/cluster-create-size.png b/static/user/getting-started/cluster-create-size.png new file mode 100644 index 00000000..07685a81 Binary files /dev/null and b/static/user/getting-started/cluster-create-size.png differ diff --git a/static/user/getting-started/cluster-created-show.png b/static/user/getting-started/cluster-created-show.png new file mode 100644 index 00000000..af4ad7eb Binary files /dev/null and b/static/user/getting-started/cluster-created-show.png differ diff --git a/static/user/getting-started/cluster-list-create.png b/static/user/getting-started/cluster-list-create.png new file mode 100644 index 00000000..b3ef983e Binary files /dev/null and b/static/user/getting-started/cluster-list-create.png differ diff --git a/static/user/getting-started/cluster-postcreate-list.png b/static/user/getting-started/cluster-postcreate-list.png new file mode 100644 index 00000000..9c284095 Binary files /dev/null and b/static/user/getting-started/cluster-postcreate-list.png differ diff --git a/static/user/getting-started/cluster-postcreate-show.png b/static/user/getting-started/cluster-postcreate-show.png new file mode 100644 index 00000000..22255753 Binary files /dev/null and b/static/user/getting-started/cluster-postcreate-show.png differ diff --git a/static/user/getting-started/cluster-template-list.png b/static/user/getting-started/cluster-template-list.png new file mode 100644 index 00000000..2bfaf6ac Binary files /dev/null and b/static/user/getting-started/cluster-template-list.png differ diff --git a/user/configs/index.html b/user/configs/index.html new file mode 100644 index 00000000..82f1d4b7 --- /dev/null +++ b/user/configs/index.html @@ -0,0 +1,656 @@ + + + + + + + + + + + + + + + + + + + + + + Configuration Options - Cluster API driver for Magnum + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Configuration Options

+

The Cluster API driver for Magnum extends magnum configuration by adding these +driver-specific configuration options.

+

auto_scaling

+

Options under this group are used for auto scaling.

+
+
image_repository
+
+

Image repository for the cluster auto-scaler. +Type: string +Default value: registry.k8s.io/autoscaling

+
+
v1_22_image
+
+

Image for the cluster auto-scaler for Kubernetes v1.22. +Type: string +Default value: $image_repository/cluster-autoscaler:v1.22.3

+
+
v1_23_image
+
+

Image for the cluster auto-scaler for Kubernetes v1.23. +Type: string +Default value: $image_repository/cluster-autoscaler:v1.23.1

+
+
v1_24_image
+
+

Image for the cluster auto-scaler for Kubernetes v1.24. +Type: string +Default value: $image_repository/cluster-autoscaler:v1.24.2

+
+
v1_25_image
+
+

Image for the cluster auto-scaler for Kubernetes v1.25. +Type: string +Default value: $image_repository/cluster-autoscaler:v1.25.2

+
+
v1_26_image
+
+

Image for the cluster auto-scaler for Kubernetes v1.26. +Type: string +Default value: $image_repository/cluster-autoscaler:v1.26.3

+
+
v1_27_image
+
+

Image for the cluster auto-scaler for Kubernetes v1.27. +Type: string +Default value: $image_repository/cluster-autoscaler:v1.27.2

+
+
+

manila_client

+

Options under this group are used for configuring Manila client.

+
+
region_name
+
+

Region in Identity service catalog to use for communication with the OpenStack service. +Type: string

+
+
endpoint_type
+
+

Type of endpoint in Identity service catalog to use for communication with the OpenStack service. +Type: string +Default value: publicURL

+
+
api_version
+
+

Version of Manila API to use in manilaclient. +Type: string +Default value: 3

+
+
ca_file
+
+

Optional CA cert file to use in SSL connections. +Type: string

+
+
cert_file
+
+

Optional PEM-formatted certificate chain file. +Type: string

+
+
key_file
+
+

Optional PEM-formatted file that contains the private key. +Type: string

+
+
insecure
+
+

If set, then the server's certificate will not be verified. +Type: boolean +Default value: False

+
+
+

capi_client

+

Options under this group are used for configuring Openstack authentication for CAPO.

+
+
endpoint_type
+
+

Type of endpoint in Identity service catalog to use for communication with the OpenStack service. +Type: string +Default value: publicURL

+
+
ca_file
+
+

Optional CA cert file to use in SSL connections. +Type: string

+
+
insecure
+
+

If set, then the server's certificate will not be verified. +Type: boolean +Default value: False

+
+
+ + + + + + +
+
+ + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/user/getting-started/index.html b/user/getting-started/index.html new file mode 100644 index 00000000..38dc1b73 --- /dev/null +++ b/user/getting-started/index.html @@ -0,0 +1,871 @@ + + + + + + + + + + + + + + + + + + + + Getting Started - Cluster API driver for Magnum + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Getting Started

+

Cluster Operations

+

Creating

+

You can use a few different methods to create a Kubernetes cluster with the +Cluster API driver for Magnum. We cover a few different methods in this +section.

+
+

Notes about deployment speed

+

The Cluster API driver for Magnum is designed to be fast. It is capable of +deploying a Kubernetes cluster in under 5 minutes. However, there are several +factors that can slow down the deployment process:

+
    +
  • +

    Operating system image size + The average size of the operating system image is around 4 GB. The image + needs to be downloaded to each node before deploying the cluster, and the + download speed depends on the network connection. The compute service caches + images locally, so the initial cluster deployment is slower than subsequent + deployments.

    +
  • +
  • +

    Network connectivity + When the cluster goes up, it needs to pull all the container images from the + container registry. By default, it will pull all the images from the upstream + registries. If you have a slow network connection, you can use a local + registry to speed up the deployment process and read more about pointing to + it in the Labels section.

    +
  • +
+

Atmosphere deploys a local +registry by default as well as includes several speed optimizations to +improve the deployment speed down to 5 minutes.

+
+

You can create clusters using several different methods which all end up using +the Magnum API. You can either use the OpenStack CLI, OpenStack Horizon +dashboard, Terraform, Ansible or the Magnum API directly.

+
+
+
+

The OpenStack CLI is the easiest way to create a Kubernetes cluster from +your terminal directly. You can use the openstack coe cluster create +command to create a Kubernetes cluster with the Cluster API driver for Magnum.

+

Before you get started, you'll have to make sure that you have the cluster +templates you want to use available in your environment. You can create +them using the OpenStack CLI:

+
export OS_DISTRO=ubuntu # you can change this to "flatcar" if you want to use Flatcar
+for version in v1.24.16 v1.25.12 v1.26.7 v1.27.4; do \
+  [[ "${OS_DISTRO}" == "ubuntu" ]] && IMAGE_NAME="ubuntu-2204-kube-${version}" || IMAGE_NAME="flatcar-kube-${version}"; \
+  curl -LO https://object-storage.public.mtl1.vexxhost.net/swift/v1/a91f106f55e64246babde7402c21b87a/magnum-capi/${IMAGE_NAME}.qcow2; \
+  openstack image create ${IMAGE_NAME} --disk-format=qcow2 --container-format=bare --property os_distro=${OS_DISTRO} --file=${IMAGE_NAME}.qcow2; \
+  openstack coe cluster template create \
+      --image $(openstack image show ${IMAGE_NAME} -c id -f value) \
+      --external-network public \
+      --dns-nameserver 8.8.8.8 \
+      --master-lb-enabled \
+      --master-flavor m1.medium \
+      --flavor m1.medium \
+      --network-driver calico \
+      --docker-storage-driver overlay2 \
+      --coe kubernetes \
+      --label kube_tag=${version} \
+      k8s-${version};
+done;
+
+

Once you've got a cluster template, you can create a cluster using the +OpenStack CLI:

+
$ openstack coe cluster create --cluster-template <cluster-template-name> <cluster-name>
+
+

You'll be able to view the status of the deployment using the OpenStack CLI:

+
$ openstack coe cluster show <cluster-name>
+
+
+
+

The OpenStack Horizon dashboard is the easiest way to create a Kubernetes +using a simple web interface. In order to get started, you can review the +list of current cluster templates in your environment by navigating using +the left sidebar to Project > Container Infra > Cluster Templates.

+

Cluster template list

+

In order to launch an new cluster, you will need to navigate to Project > +Container Infra > Clusters and click on the Launch Cluster button.

+

Cluster list with create button

+

There is a set of required fields that you will need to fill out in order +to launch a cluster, the first of which are related to it's basic +configuration, the required fields are:

+
    +
  • +

    Cluster Name
    + The name of the cluster that will be created.

    +
  • +
  • +

    Cluster Template
    + The cluster template that will be used to create the cluster.

    +
  • +
  • +

    Keypair
    + The SSH key pair that will be used to access the cluster.

    +
  • +
+

In this example, we're going to create a cluster with the name of +test-cluster, running Kuberentes 1.27.3 so using the k8s-v1.27.3 +cluster template, and using the admin_key SSH key pair.

+

Cluster create information

+

The next step is deciding on the size of the cluster and selecting if auto +scaling will be enabled for the cluster. The required fields are:

+
    +
  • +

    Number of Master Nodes
    + The number of master nodes that will be created in the cluster.

    +
  • +
  • +

    Flavor of Master Nodes
    + The flavor of the master nodes that will be created in the cluster.

    +
  • +
  • +

    Number of Worker Nodes
    + The number of worker nodes that will be created in the cluster.

    +
  • +
  • +

    Flavor of Worker Nodes
    + The flavor of the worker nodes that will be created in the cluster.

    +
  • +
+

In addition, if you want to enable auto scaling, you will need to provide the +following information:

+
    +
  • +

    Auto-scale Worker Nodes
    + Whether or not to enable auto scaling for the worker nodes.

    +
  • +
  • +

    Minimum Number of Worker Nodes + The minimum number of worker nodes that will be created in the cluster, + the auto scaler will not scale below this number even if the cluster is + under utilized.

    +
  • +
  • +

    Maximum Number of Worker Nodes + The maximum number of worker nodes that will be created in the cluster, + the auto scaler will not scale above this number even if the cluster is + over utilized.

    +
  • +
+

In this example, we're going to create a cluster with 3 master node and 4 +worker nodes, using the m1.medium flavor for both the master and worker +nodes, and we will enable auto scaling with a minimum of 2 worker nodes and +a maximum of 10 worker nodes.

+

Cluster create size

+

The next step is managing the network configuration of the cluster. The +required fields are:

+
    +
  • +

    Enable Load Balancer for Master Nodes + This is required to be enabled for the Cluster API driver for Magnum + to work properly.

    +
  • +
  • +

    Create New Network + This will determine if a new network will be created for the cluster or if + an existing network will be used. It's useful to use an existing network + if you want to attach the cluster to an existing network with other + resources.

    +
  • +
  • +

    Cluster API
    + This setting controls if the API will get a floating IP address assigned + to it. You can set this to Accessible on private network only if you + are using an existing network and don't want to expose the API to the + public internet. Otherwise, you should set it to Accessible on the public + internet to allow access to the API from the external network.

    +
  • +
+

In this example, we're going to make sure we have the load balancer enabled +for the master nodes, we're going to create a new network for the cluster, +and we're going to make sure that the API is accessible on the public internet.

+

Cluster create network

+

For the next step, we need to decide if we want to enable auto-healing for +the cluster which automatically detects nodes that are unhealthy and +replaces them with new nodes. The required fields are:

+
    +
  • Automatically Repair Unhealthy Nodes
    + Whether or not to enable auto-healing for the cluster.
  • +
+

In this example, we're going to enable auto-healing for the cluster since it +will help keep the cluster healthy.

+

Cluster create management

+

Finally, you can override labels for the cluster in the Advanced section, +we do not recommend changing these unless you know what you're doing. Once +you're ready, you can click on the Submit button to create the cluster. +The page will show your cluster being created.

+

Cluster list after creation

+

If you click on the cluster, you'll be able to track the progress of the +cluster creation, more specifically in the Status Reason field, seen below:

+

Cluster show after creation

+

Once the cluster is created, you'll be able to see the cluster details, +including the health status as well:

+

Cluster show after creation

+
+
+
+

At this point, you should have a ready cluster and you can proceed to the +Accessing section to learn how to access the cluster.

+

Accessing

+

In order to access the Kubernetes cluster, you will have to request for a +KUBECONFIG file generated by the Cluster API driver for Magnum. You can do +this using a few several ways, we cover a few of them in this section.

+
+
+
+

You can use the OpenStack CLI to request a KUBECONFIG file for a +Kubernetes cluster. You can do this using the openstack coe cluster config +command:

+
$ openstack coe cluster config <cluster-name>
+
+
+
+
+

Upgrading

+

The Cluster API driver for Magnum supports upgrading Kubernetes clusters to any +minor release in the same series or one major release ahead. The upgrade +process is performed in-place, meaning that the existing cluster is upgraded to +the new version without creating a new cluster in a rolling fashion.

+
+

Note

+

You must have an operating system image for the new Kubernetes version +available in Glance before upgrading the cluster. See the Images +documentation for more information.

+
+

In order to upgrade a cluster, you must have a cluster template pointing at the +image for the new Kubernetes version and the kube_tag label must be updated +to point at the new Kubernetes version.

+
+
+
+

Once you have this cluster template, you can trigger an upgrade by using the +OpenStack CLI:

+
$ openstack coe cluster upgrade <cluster-name> <cluster-template-name>
+
+
+
+
+

Node group role

+

Roles can be used to show the purpose of a node group, and multiple node groups can be given the same role if they share a common purpose. +

$ openstack coe nodegroup create kube test-ng --node-count 1 --role test
+
+When listing node groups, the role may be used as a filter: +
$ openstack coe nodegroup list kube --role test
++--------------------------------------+---------+-----------+--------------------------------------+------------+-----------------+------+
+| uuid                                 | name    | flavor_id | image_id                             | node_count | status          | role |
++--------------------------------------+---------+-----------+--------------------------------------+------------+-----------------+------+
+| c8acbb1f-2fa3-4d1f-b583-9a2df1e269d7 | test-ng | m1.medium | ef107f29-8f26-474e-8f5f-80d269c7d2cd |          1 | CREATE_COMPLETE | test |
++--------------------------------------+---------+-----------+--------------------------------------+------------+-----------------+------+
+
+The node group role will default to "worker" if unset, and the only reserved role is "master". +Role information is available within Kubernetes. +
$ kubectl get nodes
+NAME                                          STATUS   ROLES                  AGE     VERSION
+kube-7kjbp-control-plane-vxtrz-nhjr2          Ready    control-plane,master   3d      v1.25.3
+kube-7kjbp-default-worker-infra-hnk8x-v6cp9   Ready    worker                 2d19h   v1.25.3
+kube-7kjbp-test-ng-infra-b8yux-3v6fd          Ready    test                   5m      v1.25.3
+
+This information can be used for scheduling, using a node selector. +
nodeSelector:
+  # node-role.kubernetes.io/ROLE_NAME: ""
+  node-role.kubernetes.io/test: ""
+
+The label node.cluster.x-k8s.io/nodegroup is also available for selecting a specific node group. +
nodeSelector:
+  # node.cluster.x-k8s.io/nodegroup: "NODEGROUP_NAME"
+  node.cluster.x-k8s.io/nodegroup: "test-ng"
+

+

Reference

+

Here reference awesome blog:

+ + + + + + + +
+
+ + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/user/images/index.html b/user/images/index.html new file mode 100644 index 00000000..3a6d48e7 --- /dev/null +++ b/user/images/index.html @@ -0,0 +1,561 @@ + + + + + + + + + + + + + + + + + + + + + + Images - Cluster API driver for Magnum + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Images

+

Operating System Images

+

The Cluster API driver for Magnum relies on specific OpenStack images containing +all necessary dependencies for deploying Kubernetes clusters. These images are +pre-configured with Kubernetes binaries, container runtimes, networking +components, and other required software.

+

The images used by the Cluster API driver for Magnum are built using the +kubernetes-sigs/image-builder +project. This project provides a comprehensive and flexible framework for +constructing Kubernetes-specific images.

+

Building Images

+

In order to simplify the process of building images, the Cluster API driver for +Magnum provides a small Python utility which wraps the image-builder project.

+

To build the images, run the following command:

+
$ pip install magnum-cluster-api
+$ magnum-cluster-api-image-builder --version v1.26.2
+
+

In the example above, this command will build the images for Kubernetes version +v1.26.2. The --version flag is optional and defaults to v1.26.2.

+ + + + + + +
+
+ + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/user/labels/index.html b/user/labels/index.html new file mode 100644 index 00000000..51dec0ad --- /dev/null +++ b/user/labels/index.html @@ -0,0 +1,960 @@ + + + + + + + + + + + + + + + + + + + + + + Labels - Cluster API driver for Magnum + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Labels

+

Magnum cluster template labels are key-value pairs that are used to provide +metadata and configuration information for Kubernetes clusters created through +Magnum.

+

They can be used to define characteristics such as the operating system, +networking settings, container runtime, Kubernetes version, or any other custom +attributes relevant to the cluster deployment.

+

Volumes

+

If you require your cluster to have the root filesystem on a volume, you can +specify the volume size and type using the following labels:

+
+
boot_volume_size
+
+

The size in gigabytes of the boot volume. If you set this value, it will +enable boot from volume. +Default value: Unset

+
+
boot_volume_type
+
+

The volume type of the boot volume. +Default value: Default volume

+
+
etcd_volume_size
+
+

The size in gigabytes of the etcd volume. If you set this value, it will +create a volume for etcd specifically and mount it on the system. +Default value: Unset

+
+
etcd_volume_type
+
+

The volume type of the etcd volume, this can be useful if you want to use an +encrypted or high performance volume type. +Default value: None

+
+
+
+

Note

+

Volume labels cannot be changed once the cluster is deployed. However, you +generally do not need a large boot volume since the root filesystem is +only used for the operating system and container runtime.

+
+

Images

+

The Cluster API driver for Magnum relies on specific container images for the +deployment process.

+
+
container_infra_prefix
+
+

The prefix of the container images to use for the cluster. +Default value: None, defaults to upstream images.

+
+
+

Network

+

The way containers talk to each other and the outside world is defined by the networking setup. +This setup decides how information is shared among containers inside and outside the cluster, and +is often accomplished by deploying a driver on each node.

+
+
calico_ipv4pool
+
+

IPv4 network in CIDR format. +It refers to the IPv4 address pool used by the Calico network plugin for allocating IP addresses to pods in Kubernetes clusters. +Default value: 10.100.0.0/16.

+
+
service_cluster_ip_range
+
+

IPv4 network in CIDR format. +Defines the range of IP addresses allocated for Kubernetes services within clusters managed by Magnum. +These IP addresses are used to expose and connect services. +Default value: 10.254.0.0/16

+
+
+

Auditing

+
    +
  • audit_log_enabled
  • +
+

Enable audit logs for the cluster. The audit logs are stored in the + /var/log/kubernetes/audit/kube-apiserver-audit.log file on the control + plane hosts.

+

Default value: false

+
    +
  • audit_log_maxage
  • +
+

The number of days to retain audit logs. This is only effective if the + audit_log_enabled label is set to true.

+

Default value: 30

+
    +
  • audit_log_maxbackup
  • +
+

The maximum number of audit log files to retain. This is only effective if + the audit_log_enabled label is set to true.

+

Default value: 10

+
    +
  • audit_log_maxsize
  • +
+

The maximum size in megabytes of the audit log file before it gets rotated. + This is only effective if the audit_log_enabled label is set to true.

+

Default value: 100

+

Cloud Controller Manager

+
    +
  • cloud_provider_tag
  • +
+

The tag to use for the OpenStack cloud controller provider when bootstrapping + the cluster.

+

Default value: Automatically detected based on kube_tag label.

+
    +
  • octavia_provider
  • +
+

The Octavia provider to configure for the load balancers created by the cluster.

+

Default value: amphora

+
    +
  • octavia_lb_algorithm
  • +
+

The Octavia load balancer algorithm to configure for the load balancers + created by the cluster (options are ROUND_ROBIN, LEAST_CONNECTIONS, + SOURCE_IP & SOURCE_IP_PORT).

+

It's important to note that the OVN provider supports only the SOURCE_IP_PORT + driver as part of it's limitations.

+

Default value (amphora provider): ROUND_ROBIN + Default value (ovn provider): SOURCE_IP_PORT

+

Container Networking Interface (CNI)

+

Calcio

+
    +
  • calico_tag
  • +
+

The version of the Calico container image to use when bootstrapping the + cluster.

+

Default value: v3.24.2

+

Container Storage Interface (CSI)

+

Cinder

+
    +
  • cinder_csi_plugin_tag
  • +
+

The version of the Cinder CSI container image to use when bootstrapping the + cluster.

+

Default value: Automatically detected based on kube_tag label.

+

Manila

+
    +
  • manila_csi_plugin_tag
  • +
+

The version of the Manila CSI container image to use when bootstrapping the + cluster.

+

Default value: Automatically detected based on kube_tag label.

+
    +
  • manila_csi_share_network_id
  • +
+

Manila share network ID.

+

Default value: None

+

Kubernetes

+
    +
  • api_server_cert_sans
  • +
+

Specify the additional Subject Alternative Names (SANs) for the Kubernetes API Server, + separated by commas.

+
    +
  • api_server_tls_cipher_suites
  • +
+

Specify the list of TLS cipher suites to use for the Kubernetes API server, + separated by commas. If not specified, the default list of cipher suites + will be used using the Mozilla SSL Configuration Generator.

+

Default value: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305

+
    +
  • auto_healing_enabled
  • +
+

Enable auto-healing for the cluster. This will automatically replace failed + nodes in the cluster with new nodes (after 5 minutes of not being ready) + and stops further remediation if more than 40% of the cluster is unhealthy.

+

Default value: true

+
    +
  • auto_scaling_enabled
  • +
+

Enable auto-scaling for the cluster. This will automatically scale the + cluster up and down based on the number of pods running in the cluster.

+

Default value: false

+
    +
  • kubelet_tls_cipher_suites
  • +
+

Specify the list of TLS cipher suites to use in communication between the + kubelet and applications, separated by commas. If not specified, the + default list of cipher suites will be used.

+

Default value: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305

+
    +
  • kube_tag
  • +
+

The version of Kubernetes to use.

+

Default value: v1.25.3

+
    +
  • master_lb_floating_ip_enabled
  • +
+

Attach a floating IP to the load balancer that fronts the Kubernetes API + servers. In order to disable this, you must be running the + magnum-cluster-api-proxy service on all your Neutron network nodes.

+

Default value: true

+

OIDC

+
    +
  • oidc_issuer_url
  • +
+

The URL of the OpenID issuer, only HTTPS scheme will be accepted. If set, it + will be used to verify the OIDC JSON Web Token (JWT).

+

Default value: ``

+
    +
  • oidc_client_id
  • +
+

The client ID for the OpenID Connect client, must be set if oidc_issuer_url + is set.

+

Default value: ``

+
    +
  • oidc_username_claim
  • +
+

The OpenID claim to use as the user name.

+

Default value: sub

+
    +
  • oidc_username_prefix
  • +
+

If provided, all usernames will be prefixed with this value. If not provided, + username claims other than 'email' are prefixed by the issuer URL to avoid + clashes. To skip any prefixing, use the default value.

+

Default value: -

+
    +
  • oidc_groups_claim
  • +
+

If provided, the name of a custom OpenID Connect claim for specifying user + groups. The claim value is expected to be a string or array of strings.

+

Default value: ``

+
    +
  • oidc_groups_prefix
  • +
+

If provided, all groups will be prefixed with this value to prevent conflicts + with other authentication strategies.

+

Default value: ``

+

OpenStack

+
    +
  • fixed_subnet_cidr
  • +
+

The CIDR of the fixed subnet to use for the cluster.

+

Default value: 10.0.0.0/24

+

TODO

+

availability_zone +dns_cluster_domain +calico_ipv4pool

+ + + + + + +
+
+ + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file