PageSourceSearch

https://flanksource.com/assets/js/2c65ac8e.e9813c51.js

js flanksource.com collected 2026-09-25 15:25:28 UTC 13,421 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunkmission_control=globalThis.webpackChunkmission_control||[]).push([[2520],{53632(e,n,t){t.d(n,{A:()=>l});t(96540);var s=t(34164),r=t(64924);const i="tabItem_Ymn6";var a=t(74848);function o({children:e,className:n,hidden:t}){return(0,a.jsx)("div",{role:"tabpanel",className:(0,s.A)(i,n),hidden:t,children:e})}function l({children:e,className:n,value:t}){const{selectedValue:s,lazy:i}=(0,r.uc)(),l=t===s;return!l&&i?null:(0,a.jsx)(o,{className:n,hidden:!l,children:e})}},63906(e,n,t){t.d(n,{A:()=>m});t(96540);var s=t(34164),r=t(79872),i=t(64924),a=t(44319),o=t(46916);const l="tabList__CuJ",c="tabItem_LNqP";var u=t(74848);function d({className:e}){const{selectedValue:n,selectValue:t,tabValues:r,block:o}=(0,i.uc)(),l=[],{blockElementScrollPositionUntilNextRender:d}=(0,a.a_)(),h=e=>{const s=e.currentTarget,i=l.indexOf(s),a=r[i].value;a!==n&&(d(s),t(a))},p=e=>{let n=null;switch(e.key){case"Enter":h(e);break;case"ArrowRight":{const t=l.indexOf(e.currentTarget)+1;n=l[t]??l[0];break}case"ArrowLeft":{const t=l.indexOf(e.currentTarget)-1;n=l[t]??l[l.length-1];break}}n?.focus()};return(0,u.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,s.A)("tabs",{"tabs--block":o},e),children:r.map(({value:e,label:t,attributes:r})=>(0,u.jsx)("li",{role:"tab",tabIndex:n===e?0:-1,"aria-selected":n===e,ref:e=>{l.push(e)},onKeyDown:p,onClick:h,...r,className:(0,s.A)("tabs__item",c,r?.className,{"tabs__item--active":n===e}),children:t??e},e))})}function h({children:e}){return(0,u.jsx)("div",{className:"margin-top--md",children:e})}function p({className:e,children:n}){return(0,u.jsxs)("div",{className:(0,s.A)(r.G.tabs.container,"tabs-container",l),children:[(0,u.jsx)(d,{className:e}),(0,u.jsx)(h,{children:n})]})}function m(e){const n=(0,o.A)(),t=(0,i.OC)(e);return(0,u.jsx)(i.O_,{value:t,children:(0,u.jsx)(p,{className:e.className,children:(0,i.vT)(e.children)})},String(n))}},64924(e,n,t){t.d(n,{OC:()=>m,O_:()=>b,uc:()=>g,vT:()=>u});var s=t(96540),r=t(56347),i=t(94280),a=t(73024),o=t(44031),l=t(58417),c=t(74848);function u(e){return s.Children.toArray(e).filter(e=>"\n"!==e)}function d(e){const{values:n,children:t}=e;return(0,s.useMemo)(()=>{const e=n??function(e){return s.Children.toArray(e).flatMap(e=>{if(!e)return[];if((0,s.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return[e];const n="string"==typeof e.type?e.type:e.type.name;throw new Error(`Docusaurus error: Bad <Tabs> child <${n}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.\nIf you do not want to pass on a "value" prop to the direct children of <Tabs>, you can also pass an explicit <Tabs values={...}> prop.`)}).map(({props:{value:e,label:n,attributes:t,default:s}})=>({value:e,label:n,attributes:t,default:s}))}(t);return function(e){const n=(0,l.XI)(e,(e,n)=>e.value===n.value);if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map(e=>`'${e.value}'`).join(", ")}" found in <Tabs>. Every value needs to be unique.`)}(e),e},[n,t])}function h({value:e,tabValues:n}){return n.some(n=>n.value===e)}function p({queryString:e=!1,groupId:n}){const t=(0,r.W6)(),i=function({queryString:e=!1,groupId:n}){if("string"==typeof e)return e;if(!1===e)return null;if(!0===e&&!n)throw new Error('Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return n??null}({queryString:e,groupId:n});return[(0,a.aZ)(i),(0,s.useCallback)(e=>{if(!i)return;const n=new URLSearchParams(t.location.search);n.set(i,e),t.replace({...t.location,search:n.toString()})},[i,t])]}function m(e){const{defaultValue:n,queryString:t=!1,groupId:r}=e,a=d(e),[l,c]=(0,s.useState)(()=>function({defaultValue:e,tabValues:n}){if(0===n.length)throw new Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(e){if(!h({value:e,tabValues:n}))throw new Error(`Docusaurus error: The <Tabs> has a defaultValue "${e}" but none of its children has the corresponding value. Available values are: ${n.map(e=>e.value).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return e}const t=n.find(e=>e.default)??n[0];if(!t)throw new Error("Unexpected error: 0 tabValues");return t.value}({defaultValue:n,tabValues:a})),[u,m]=p({queryString:t,groupId:r}),[f,g]=function({groupId:e}){const n=function(e){return e?`docusaurus.tab.${e}`:null}(e),[t,r]=(0,o.Dv)(n);return[t,(0,s.useCallback)(e=>{n&&r.set(e)},[n,r])]}({groupId:r}),b=(()=>{const e=u??f;return h({value:e,tabValues:a})?e:null})();(0,i.A)(()=>{b&&c(b)},[b]);return{selectedValue:l,selectValue:(0,s.useCallback)(e=>{if(!h({value:e,tabValues:a}))throw new Error(`Can't select invalid tab value=${e}`);c(e),m(e),g(e)},[m,g,a]),tabValues:a,lazy:e.lazy??!1,block:e.block??!1}}const f=(0,s.createContext)(null);function g(){const e=s.useContext(f);if(!e)throw new Error("useTabsContext() must be used within a Tabs component");return e}function b(e){return(0,c.jsx)(f.Provider,{value:e.value,children:e.children})}},19725(e,n,t){t.r(n),t.d(n,{assets:()=>u,contentTitle:()=>c,default:()=>p,frontMatter:()=>l,metadata:()=>s,toc:()=>d});const s=JSON.parse('{"id":"installation/local-testing","title":"Local Testing","description":"Run Mission Control Locally using minikube or kind","source":"@site/docs/installation/local-testing.md","sourceDirName":"installation","slug":"/installation/local-testing","permalink":"/docs/installation/local-testing","draft":false,"unlisted":false,"editUrl":"https://github.com/flanksource/docs/tree/main/docs/installation/local-testing.md","tags":[],"version":"current","frontMatter":{"title":"Local Testing","description":"Run Mission Control Locally using minikube or kind","sidebar_custom_props":{"icon":"lab"}},"sidebar":"overview","previous":{"title":"GCP GKE","permalink":"/docs/installation/self-hosted/gke"},"next":{"title":"Architecture","permalink":"/docs/architecture"}}');var r=t(74848),i=t(28453),a=t(63906),o=t(53632);const l={title:"Local Testing",description:"Run Mission Control Locally using minikube or kind",sidebar_custom_props:{icon:"lab"}},c=void 0,u={},d=[];function h(e){const n={a:"a",admonition:"admonition",code:"code",li:"li",p:"p",pre:"pre",ul:"ul",...(0,i.R)(),...e.components},{Step:t}=n;return t||function(e,n){throw new Error("Expected "+(n?"component":"object")+" `"+e+"` to be defined: you likely forgot to import, pass, or provide it.")}("Step",!0),(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.admonition,{title:"Prerequisites",type:"info",children:(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"kubectl"}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"https://helm.sh/docs/intro/install/",children:"helm"})," v3+"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"https://kind.sigs.k8s.io/docs/user/quick-start/",children:"kind"})," or ",(0,r.jsx)(n.a,{href:"https://minikube.sigs.k8s.io/docs/start/",children:"minikube"})]}),"\n",(0,r.jsx)(n.li,{children:"20GB+ free space for docker volumes"}),"\n"]})}),"\n",(0,r.jsxs)(a.A,{children:[(0,r.jsxs)(o.A,{label:"Kind",value:"Kind",children:[(0,r.jsxs)(t,{step:1,name:"Create kind cluster",children:[(0,r.jsx)(n.p,{children:"The demo requires access to mission control via an ingress configuration. To route traffic into the kind cluster, it must be configured with a binding to a port on the host."}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-yaml",metastring:'title="kind.config"',children:'kind: Cluster\n
1apiVersion: kind.x-k8s.io/v1alpha4\nnodes:\n  - role: control-plane\n    kubeadmConfigPatches:\n      - |\n        kind: InitConfiguration\n        nodeRegistration:\n          kubeletExtraArgs:\n            node-labels: "ingress-ready=true"\n    extraPortMappings:\n      - containerPort: 80\n        hostPort: 8080\n        protocol: TCP\n      - containerPort: 443\n        hostPort: 8443\n        protocol: TCP\n'})}),(0,r.jsxs)(n.p,{children:["A single node cluster is provisioned, hosting both the control plane and workloads. Configure the ",(0,r.jsx)(n.code,{children:"hostPort"})," bindings onto free ports, in this case ",(0,r.jsx)(n.code,{children:"8080"})," and ",(0,r.jsx)(n.code,{children:"8443"})]}),(0,r.jsx)(n.p,{children:"Provision the kind cluster with"}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"kind create cluster --config kind.config\n"})}),(0,r.jsx)(n.p,{children:"verify the cluster is running by using:"}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"kubectl get nodes\n"})})]}),(0,r.jsxs)(t,{step:2,name:"Provision Ingress controller",children:[(0,r.jsxs)(n.p,{children:["Install ",(0,r.jsx)(n.a,{href:"https://github.com/kubernetes/ingress-nginx",children:"ingress-nginx"})," controller with:"]}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/kind/deploy.yaml\n"})}),(0,r.jsx)(n.p,{children:"Confirm that the ingress controller pod is running with:"}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"kubectl get pods -n ingress-nginx\n"})})]})]}),(0,r.jsxs)(o.A,{label:"Minikube",value:"minikube",children:[(0,r.jsx)(t,{step:1,name:"Create minikube cluster",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{children:"minikube start\n"})})}),(0,r.jsx)(t,{step:2,name:"Provision Ingress controller",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{children:"minikube addons configure ingress\nminikube addons enable ingress\n"})})})]})]}),"\n",(0,r.jsxs)(t,{step:3,name:"Install Mission Control",children:[(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.a,{href:"http://nip.io",children:"nip.io"})," is a wildcard DNS server that returns the ip provided in the host name, e.g."]}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"\u276f nslookup  127.0.0.1.nip.io\nAddress: 127.0.0.1\n"})}),(0,r.jsx)(n.p,{children:"By using nip you can access mission-control without any further networking / configuration setup."}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-yaml",metastring:'title="values.yaml"',children:"global:\n  ui:\n    host: 127.0.0.1.nip.io\nresources:\n  requests:\n    cpu: 10m\n    memory: 100Mi\ndb:\n  conf:\n    shared_buffers: 128MB\n    work_mem: 10MB\n  resources:\n    requests:\n      cpu: 10m\n      memory: 256Mi\ncanary-checker:\n  resources:\n    requests:\n      cpu: 10m\n      memory: 128Mi\nconfig-db:\n  resources:\n    requests:\n      cpu: 10m\n      memory: 128Mi\n"})}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"helm repo add flanksource https://flanksource.github.io/charts\nhelm repo update\nhelm install mission-control  \\\n  flanksource/mission-control  \\\n -n mission-control \\\n --create-namespace \\\n --wait \\\n -f values.yaml\n"})}),(0,r.jsxs)(n.p,{children:["See ",(0,r.jsx)(n.a,{href:"/docs/installation/helm#self-hosted",children:"values.yaml"})," for more options."]})]}),"\n",(0,r.jsxs)(t,{step:4,name:"Login",children:[(0,r.jsxs)(n.p,{children:["The default username is ",(0,r.jsx)(n.code,{children:"admin@local"})," and the password can be retrieved with:"]}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{children:"kubectl get secret  mission-control-admin-password \\\n  -n mission-control \\\n  --template='{{.data.password | base64decode}}'\n"})}),(0,r.jsxs)(n.p,{children:["You can then go to ",(0,r.jsx)(n.a,{href:"https://127.0.0.1.nip.io:8443",children:"https://127.0.0.1.nip.io:8443"})," to login."]}
1),(0,r.jsx)(n.admonition,{title:"Self-Signed Certificate",type:"info",children:(0,r.jsxs)(n.p,{children:["This example uses a self-signed certificate created by nginx, We recommend using ",(0,r.jsx)(n.a,{href:"https://cert-manager.io/",children:"cert-manager.io"}),"."]})})]}),"\n",(0,r.jsxs)(t,{step:5,name:"Taking your first step, creating a health check",children:[(0,r.jsx)(n.p,{children:"Create a file containing canary definitions, for example:"}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-yaml",metastring:"title=canaries.yaml",children:"apiVersion: canaries.flanksource.com/v1\nkind: Canary\nmetadata:\n  name: http\nspec:\n  interval: 30\n  http:\n    - url: https://httpstat.us/200\n      name: 'httpstat healthy'\n"})}),(0,r.jsx)(n.p,{children:"and apply to the cluster with:"}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"kubectl apply -f canaries.yaml\n"})}),(0,r.jsxs)(n.p,{children:["Navigate to the ",(0,r.jsx)(n.a,{href:"https://127.0.0.1.nip.io:8443/health",children:"Health"})," tab you can then see the check running:"]})]})]})}function p(e={}){const{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(h,{...e})}):h(e)}},28453(e,n,t){t.d(n,{R:()=>a,x:()=>o});var s=t(96540);const r={},i=s.createContext(r);function a(e){const n=s.useContext(i);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:a(e.components),s.createElement(i.Provider,{value:n},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.