1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00
lens/v5.3.1/extensions/guides/resource-stack/index.html

1038 lines
51 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Documentation for Lens Extension Development and API.">
<meta name="author" content="Mirantis, Inc.">
<link rel="canonical" href="https://api-docs.k8slens.dev/v5.3.1/extensions/guides/resource-stack/">
<link rel="icon" href="../../../img/favicon.ico">
<meta name="generator" content="mkdocs-1.2.3, mkdocs-material-7.1.8+insiders-2.9.2">
<title>Resource Stack - Lens Extension Development</title>
<link rel="stylesheet" href="../../../assets/stylesheets/main.92048cb8.min.css">
<link rel="stylesheet" href="../../../assets/stylesheets/palette.73e53a79.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,400i,700%7CRoboto+Mono&display=fallback">
<style>:root{--md-text-font-family:"Roboto";--md-code-font-family:"Roboto Mono"}</style>
<link rel="stylesheet" href="../../../stylesheets/extra.css">
<script>window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)},ga.l=+new Date,ga("create","UA-159377374-2","auto"),ga("set","anonymizeIp",!0),ga("send","pageview"),document.addEventListener("DOMContentLoaded",function(){document.forms.search&&document.forms.search.query.addEventListener("blur",function(){var e;this.value&&(e=document.location.pathname,ga("send","pageview",e+"?q="+this.value))}),"undefined"!=typeof location$&&location$.subscribe(function(e){ga("send","pageview",e.pathname)})})</script>
<script async src="https://www.google-analytics.com/analytics.js"></script>
</head>
<body dir="ltr" data-md-color-scheme="slate" data-md-color-primary="" data-md-color-accent="">
<script>function __scope(t,e="../../.."){return new URL(e,location).pathname+"."+t}function __get(t,e=localStorage,n){return JSON.parse(e.getItem(__scope(t,n)))}function __set(t,e,n=localStorage,o){try{n.setItem(__scope(t,o),JSON.stringify(e))}catch(t){}}</script>
<script>var palette=__get("__palette");if(palette&&"object"==typeof palette.color)for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)</script>
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" for="__drawer"></label>
<div data-md-component="skip">
<a href="#resource-stack-cluster-feature" class="md-skip">
Skip to content
</a>
</div>
<div data-md-component="announce">
</div>
<div data-md-component="outdated" hidden>
<aside class="md-banner md-banner--warning">
</aside>
</div>
<header class="md-header" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="Header">
<a href="../../.." title="Lens Extension Development" class="md-header__button md-logo" aria-label="Lens Extension Development" data-md-component="logo">
<img src="../../../img/lens-logo-icon.svg" alt="logo">
</a>
<label class="md-header__button md-icon" for="__drawer">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2z"/></svg>
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
Lens Extension Development
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
Resource Stack
</span>
</div>
</div>
</div>
<form class="md-header__option" data-md-component="palette">
<input class="md-option" data-md-color-media="" data-md-color-scheme="slate" data-md-color-primary="" data-md-color-accent="" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_1">
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_2" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7 10a2 2 0 0 1 2 2 2 2 0 0 1-2 2 2 2 0 0 1-2-2 2 2 0 0 1 2-2m10-3a5 5 0 0 1 5 5 5 5 0 0 1-5 5H7a5 5 0 0 1-5-5 5 5 0 0 1 5-5h10M7 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3h10a3 3 0 0 0 3-3 3 3 0 0 0-3-3H7z"/></svg>
</label>
<input class="md-option" data-md-color-media="" data-md-color-scheme="default" data-md-color-primary="" data-md-color-accent="" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_2">
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_1" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M17 7H7a5 5 0 0 0-5 5 5 5 0 0 0 5 5h10a5 5 0 0 0 5-5 5 5 0 0 0-5-5m0 8a3 3 0 0 1-3-3 3 3 0 0 1 3-3 3 3 0 0 1 3 3 3 3 0 0 1-3 3z"/></svg>
</label>
</form>
<label class="md-header__button md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5z"/></svg>
</label>
<div class="md-search" data-md-component="search" role="dialog">
<label class="md-search__overlay" for="__search"></label>
<div class="md-search__inner" role="search">
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
<label class="md-search__icon md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12z"/></svg>
</label>
<nav class="md-search__options" aria-label="Search">
<button type="reset" class="md-search__icon md-icon" aria-label="Clear" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/></svg>
</button>
</nav>
<div class="md-search__suggest" data-md-component="search-suggest"></div>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" data-md-scrollfix>
<div class="md-search-result" data-md-component="search-result">
<div class="md-search-result__meta">
Initializing search
</div>
<ol class="md-search-result__list"></ol>
</div>
</div>
</div>
</div>
</div>
<div class="md-header__source">
<a href="https://github.com/lensapp/lens" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
</div>
<div class="md-source__repository">
GitHub
</div>
</a>
</div>
</nav>
</header>
<div class="md-container" data-md-component="container">
<main class="md-main" data-md-component="main">
<div class="md-main__inner md-grid">
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
<label class="md-nav__title" for="__drawer">
<a href="../../.." title="Lens Extension Development" class="md-nav__button md-logo" aria-label="Lens Extension Development" data-md-component="logo">
<img src="../../../img/lens-logo-icon.svg" alt="logo">
</a>
Lens Extension Development
</label>
<div class="md-nav__source">
<a href="https://github.com/lensapp/lens" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
</div>
<div class="md-source__repository">
GitHub
</div>
</a>
</div>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../.." class="md-nav__link">
Overview
</a>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle" data-md-toggle="__nav_2" type="checkbox" id="__nav_2" >
<label class="md-nav__link" for="__nav_2">
Getting Started
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" aria-label="Getting Started" data-md-level="1">
<label class="md-nav__title" for="__nav_2">
<span class="md-nav__icon md-icon"></span>
Getting Started
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../get-started/overview/" class="md-nav__link">
Overview
</a>
</li>
<li class="md-nav__item">
<a href="../../get-started/your-first-extension/" class="md-nav__link">
Your First Extension
</a>
</li>
<li class="md-nav__item">
<a href="../../get-started/anatomy/" class="md-nav__link">
Extension Anatomy
</a>
</li>
<li class="md-nav__item">
<a href="../../get-started/wrapping-up/" class="md-nav__link">
Wrapping Up
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle" data-md-toggle="__nav_3" type="checkbox" id="__nav_3" >
<label class="md-nav__link" for="__nav_3">
Extension Capabilities
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" aria-label="Extension Capabilities" data-md-level="1">
<label class="md-nav__title" for="__nav_3">
<span class="md-nav__icon md-icon"></span>
Extension Capabilities
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../capabilities/common-capabilities/" class="md-nav__link">
Common Capabilities
</a>
</li>
<li class="md-nav__item">
<a href="../../capabilities/styling/" class="md-nav__link">
Styling
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--nested">
<input class="md-nav__toggle md-toggle" data-md-toggle="__nav_4" type="checkbox" id="__nav_4" checked>
<label class="md-nav__link" for="__nav_4">
Extension Guides
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" aria-label="Extension Guides" data-md-level="1">
<label class="md-nav__title" for="__nav_4">
<span class="md-nav__icon md-icon"></span>
Extension Guides
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../" class="md-nav__link">
Overview
</a>
</li>
<li class="md-nav__item">
<a href="../generator/" class="md-nav__link">
Generator
</a>
</li>
<li class="md-nav__item">
<a href="../main-extension/" class="md-nav__link">
Main Extension
</a>
</li>
<li class="md-nav__item">
<a href="../renderer-extension/" class="md-nav__link">
Renderer Extension
</a>
</li>
<li class="md-nav__item">
<a href="../catalog/" class="md-nav__link">
Catalog
</a>
</li>
<li class="md-nav__item md-nav__item--active">
<input class="md-nav__toggle md-toggle" data-md-toggle="toc" type="checkbox" id="__toc">
<a href="./" class="md-nav__link md-nav__link--active">
Resource Stack
</a>
</li>
<li class="md-nav__item">
<a href="../stores/" class="md-nav__link">
Stores
</a>
</li>
<li class="md-nav__item">
<a href="../working-with-mobx/" class="md-nav__link">
Working with MobX
</a>
</li>
<li class="md-nav__item">
<a href="../protocol-handlers/" class="md-nav__link">
Protocol Handlers
</a>
</li>
<li class="md-nav__item">
<a href="../ipc/" class="md-nav__link">
IPC
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle" data-md-toggle="__nav_5" type="checkbox" id="__nav_5" >
<label class="md-nav__link" for="__nav_5">
Testing and Publishing
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" aria-label="Testing and Publishing" data-md-level="1">
<label class="md-nav__title" for="__nav_5">
<span class="md-nav__icon md-icon"></span>
Testing and Publishing
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../testing-and-publishing/testing/" class="md-nav__link">
Testing Extensions
</a>
</li>
<li class="md-nav__item">
<a href="../../testing-and-publishing/publishing/" class="md-nav__link">
Publishing Extensions
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../../api/" class="md-nav__link">
API Reference
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
</nav>
</div>
</div>
</div>
<div class="md-content" data-md-component="content">
<article class="md-content__inner md-typeset">
<h1 id="resource-stack-cluster-feature">Resource Stack (Cluster Feature)<a class="headerlink" href="#resource-stack-cluster-feature" title="Permanent link">#</a></h1>
<p>A cluster feature is a set of Kubernetes resources that can be applied to and managed within the active cluster.
The <code>Renderer.K8sApi.ResourceStack</code> class provides the functionality to input and apply kubernetes resources to a cluster.
It is up to the extension developer to manage the life cycle of the resource stack.
It could be applied automatically to a cluster by the extension, or the end-user could be required to install it.</p>
<p>The code examples in this section show how to create a resource stack, and define a cluster feature that is configurable from the cluster <strong>Settings</strong> page.</p>
<div class="admonition info">
<p class="admonition-title">Info</p>
<p>To access the cluster <strong>Settings</strong> page, right-click the relevant cluster in the left side menu and click <strong>Settings</strong>.</p>
</div>
<p>The resource stack in this example consists of a single kubernetes resource:</p>
<div class="highlight"><pre><span></span><code><span class="nt">apiVersion</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">v1</span>
<span class="nt">kind</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">Pod</span>
<span class="nt">metadata</span><span class="p">:</span>
<span class="nt">name</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">example-pod</span>
<span class="nt">spec</span><span class="p">:</span>
<span class="nt">containers</span><span class="p">:</span>
<span class="p p-Indicator">-</span> <span class="nt">name</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">example-pod</span>
<span class="nt">image</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">nginx</span>
</code></pre></div>
<p>It is simply a pod named <code>example-pod</code>, running nginx. Assume this content is in the file <code>../resources/example-pod.yml</code>.</p>
<p>The following code sample shows how to use the <code>Renderer.K8sApi.ResourceStack</code> to manage installing and uninstalling this resource stack:</p>
<div class="highlight"><pre><span></span><code><span class="k">import</span> <span class="p">{</span> <span class="nx">Renderer</span><span class="p">,</span> <span class="nx">Common</span> <span class="p">}</span> <span class="kr">from</span> <span class="s2">&quot;@k8slens/extensions&quot;</span><span class="p">;</span>
<span class="k">import</span> <span class="o">*</span> <span class="kr">as</span> <span class="nx">path</span> <span class="kr">from</span> <span class="s2">&quot;path&quot;</span><span class="p">;</span>
<span class="kd">const</span> <span class="p">{</span>
<span class="nx">K8sApi</span><span class="o">:</span> <span class="p">{</span>
<span class="nx">ResourceStack</span><span class="p">,</span>
<span class="nx">forCluster</span><span class="p">,</span>
<span class="nx">Pod</span><span class="p">,</span>
<span class="p">}</span>
<span class="p">}</span> <span class="o">=</span> <span class="nx">Renderer</span><span class="p">;</span>
<span class="kr">type</span> <span class="nx">ResourceStack</span> <span class="o">=</span> <span class="nx">Renderer</span><span class="p">.</span><span class="nx">K8sApi</span><span class="p">.</span><span class="nx">ResourceStack</span><span class="p">;</span>
<span class="kr">type</span> <span class="nx">Pod</span> <span class="o">=</span> <span class="nx">Renderer</span><span class="p">.</span><span class="nx">K8sApi</span><span class="p">.</span><span class="nx">Pod</span><span class="p">;</span>
<span class="kr">type</span> <span class="nx">KubernetesCluster</span> <span class="o">=</span> <span class="nx">Common</span><span class="p">.</span><span class="nx">Catalog</span><span class="p">.</span><span class="nx">KubernetesCluster</span><span class="p">;</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">ExampleClusterFeature</span> <span class="p">{</span>
<span class="k">protected</span> <span class="nx">stack</span>: <span class="kt">ResourceStack</span><span class="p">;</span>
<span class="kr">constructor</span><span class="p">(</span><span class="k">protected</span> <span class="nx">cluster</span>: <span class="kt">KubernetesCluster</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">stack</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">ResourceStack</span><span class="p">(</span><span class="nx">cluster</span><span class="p">,</span> <span class="s2">&quot;example-resource-stack&quot;</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">get</span> <span class="nx">resourceFolder</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s2">&quot;../resources/&quot;</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">install</span><span class="p">()</span><span class="o">:</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="kt">string</span><span class="o">&gt;</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&quot;installing example-pod&quot;</span><span class="p">);</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">stack</span><span class="p">.</span><span class="nx">kubectlApplyFolder</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">resourceFolder</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">async</span> <span class="nx">isInstalled</span><span class="p">()</span><span class="o">:</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="kt">boolean</span><span class="o">&gt;</span> <span class="p">{</span>
<span class="k">try</span> <span class="p">{</span>
<span class="kd">const</span> <span class="nx">podApi</span> <span class="o">=</span> <span class="nx">forCluster</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">cluster</span><span class="p">,</span> <span class="nx">Pod</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">examplePod</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">podApi</span><span class="p">.</span><span class="nx">get</span><span class="p">({</span><span class="nx">name</span><span class="o">:</span> <span class="s2">&quot;example-pod&quot;</span><span class="p">,</span> <span class="nx">namespace</span><span class="o">:</span> <span class="s2">&quot;default&quot;</span><span class="p">});</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">examplePod</span><span class="o">?</span><span class="p">.</span><span class="nx">kind</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&quot;found example-pod&quot;</span><span class="p">);</span>
<span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&quot;Error getting example-pod:&quot;</span><span class="p">,</span> <span class="nx">e</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&quot;didn&#39;t find example-pod&quot;</span><span class="p">);</span>
<span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">async</span> <span class="nx">uninstall</span><span class="p">()</span><span class="o">:</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="kt">string</span><span class="o">&gt;</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&quot;uninstalling example-pod&quot;</span><span class="p">);</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">stack</span><span class="p">.</span><span class="nx">kubectlDeleteFolder</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">resourceFolder</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>The <code>ExampleClusterFeature</code> class constructor takes a <code>Common.Catalog.KubernetesCluster</code> argument.
This is the cluster that the resource stack will be applied to, and the constructor instantiates a <code>Renderer.K8sApi.ResourceStack</code> as such.
<code>ExampleClusterFeature</code> implements an <code>install()</code> method which simply invokes the <code>kubectlApplyFolder()</code> method of the <code>Renderer.K8sApi.ResourceStack</code> class.
<code>kubectlApplyFolder()</code> applies to the cluster all kubernetes resources found in the folder passed to it, in this case <code>../resources</code>.
Similarly, <code>ExampleClusterFeature</code> implements an <code>uninstall()</code> method which simply invokes the <code>kubectlDeleteFolder()</code> method of the <code>Renderer.K8sApi.ResourceStack</code> class.
<code>kubectlDeleteFolder()</code> tries to delete from the cluster all kubernetes resources found in the folder passed to it, again in this case <code>../resources</code>.</p>
<p><code>ExampleClusterFeature</code> also implements an <code>isInstalled()</code> method, which demonstrates how you can utilize the kubernetes api to inspect the resource stack status.
<code>isInstalled()</code> simply tries to find a pod named <code>example-pod</code>, as a way to determine if the pod is already installed.
This method can be useful in creating a context-sensitive UI for installing/uninstalling the feature, as demonstrated in the next sample code.</p>
<p>To allow the end-user to control the life cycle of this cluster feature the following code sample shows how to implement a user interface based on React and custom Lens UI components:</p>
<div class="highlight"><pre><span></span><code> <span class="k">import</span> <span class="nx">React</span> <span class="kr">from</span> <span class="s2">&quot;react&quot;</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">Common</span><span class="p">,</span> <span class="nx">Renderer</span> <span class="p">}</span> <span class="kr">from</span> <span class="s2">&quot;@k8slens/extensions&quot;</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">observer</span> <span class="p">}</span> <span class="kr">from</span> <span class="s2">&quot;mobx-react&quot;</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">computed</span><span class="p">,</span> <span class="nx">observable</span><span class="p">,</span> <span class="nx">makeObservable</span> <span class="p">}</span> <span class="kr">from</span> <span class="s2">&quot;mobx&quot;</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">ExampleClusterFeature</span> <span class="p">}</span> <span class="kr">from</span> <span class="s2">&quot;./example-cluster-feature&quot;</span><span class="p">;</span>
<span class="kd">const</span> <span class="p">{</span>
<span class="nx">Component</span><span class="o">:</span> <span class="p">{</span>
<span class="nx">SubTitle</span><span class="p">,</span> <span class="nx">Button</span><span class="p">,</span>
<span class="p">}</span>
<span class="p">}</span> <span class="o">=</span> <span class="nx">Renderer</span><span class="p">;</span>
<span class="kd">interface</span> <span class="nx">Props</span> <span class="p">{</span>
<span class="nx">cluster</span>: <span class="kt">Common.Catalog.KubernetesCluster</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">@observer</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">ExampleClusterFeatureSettings</span> <span class="k">extends</span> <span class="nx">React</span><span class="p">.</span><span class="nx">Component</span><span class="o">&lt;</span><span class="nx">Props</span><span class="o">&gt;</span> <span class="p">{</span>
<span class="kr">constructor</span><span class="p">(</span><span class="nx">props</span>: <span class="kt">Props</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="p">(</span><span class="nx">props</span><span class="p">);</span>
<span class="nx">makeObservable</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">@observable</span> <span class="nx">installed</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="kd">@observable</span> <span class="nx">inProgress</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="nx">feature</span>: <span class="kt">ExampleClusterFeature</span><span class="p">;</span>
<span class="k">async</span> <span class="nx">componentDidMount</span><span class="p">()</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">feature</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">ExampleClusterFeature</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">cluster</span><span class="p">);</span>
<span class="k">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">updateFeatureState</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">async</span> <span class="nx">updateFeatureState</span><span class="p">()</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">installed</span> <span class="o">=</span> <span class="k">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">feature</span><span class="p">.</span><span class="nx">isInstalled</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">async</span> <span class="nx">save</span><span class="p">()</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">inProgress</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="k">try</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">installed</span><span class="p">)</span> <span class="p">{</span>
<span class="k">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">feature</span><span class="p">.</span><span class="nx">uninstall</span><span class="p">();</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">feature</span><span class="p">.</span><span class="nx">install</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">finally</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">inProgress</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="k">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">updateFeatureState</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">@computed</span> <span class="nx">get</span> <span class="nx">buttonLabel</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">inProgress</span> <span class="o">&amp;&amp;</span> <span class="k">this</span><span class="p">.</span><span class="nx">installed</span><span class="p">)</span> <span class="k">return</span> <span class="s2">&quot;Uninstalling ...&quot;</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">inProgress</span><span class="p">)</span> <span class="k">return</span> <span class="s2">&quot;Applying ...&quot;</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">installed</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="s2">&quot;Uninstall&quot;</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="s2">&quot;Apply&quot;</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span>
<span class="o">&lt;&gt;</span>
<span class="o">&lt;</span><span class="nx">section</span><span class="o">&gt;</span>
<span class="o">&lt;</span><span class="nx">SubTitle</span> <span class="nx">title</span><span class="o">=</span><span class="s2">&quot;Example Cluster Feature using a Resource Stack&quot;</span> <span class="o">/&gt;</span>
<span class="o">&lt;</span><span class="nx">Button</span>
<span class="nx">label</span><span class="o">=</span><span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">buttonLabel</span><span class="p">}</span>
<span class="nx">waiting</span><span class="o">=</span><span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">inProgress</span><span class="p">}</span>
<span class="nx">onClick</span><span class="o">=</span><span class="p">{()</span> <span class="p">=&gt;</span> <span class="k">this</span><span class="p">.</span><span class="nx">save</span><span class="p">()}</span>
<span class="nx">primary</span> <span class="o">/&gt;</span>
<span class="o">&lt;</span><span class="err">/section&gt;</span>
<span class="o">&lt;</span><span class="err">/&gt;</span>
<span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>The <code>ExampleClusterFeatureSettings</code> class extends <code>React.Component</code> and simply renders a subtitle and a button.
<code>ExampleClusterFeatureSettings</code> takes the cluster as a prop and when the React component has mounted the <code>ExampleClusterFeature</code> is instantiated using this cluster (in <code>componentDidMount()</code>).
The rest of the logic concerns the button appearance and action, based on the <code>ExampleClusterFeatureSettings</code> fields <code>installed</code> and <code>inProgress</code>.
The <code>installed</code> value is of course determined using the aforementioned <code>ExampleClusterFeature</code> method <code>isInstalled()</code>.
The <code>inProgress</code> value is true while waiting for the feature to be installed (or uninstalled).</p>
<p>Note that the button is a <code>Renderer.Component.Button</code> element and this example takes advantage of its <code>waiting</code> prop to show a "waiting" animation while the install (or uninstall) is in progress.
Using elements from <code>Renderer.Component</code> is encouraged, to take advantage of their built-in properties, and to ensure a common look and feel.</p>
<p>Also note that <a href="https://mobx.js.org/README.html">MobX 6</a> is used for state management, ensuring that the UI is rerendered when state has changed.
The <code>ExampleClusterFeatureSettings</code> class is marked as an <code>@observer</code>, and its constructor must call <code>makeObservable()</code>.
As well, the <code>installed</code> and <code>inProgress</code> fields are marked as <code>@observable</code>, ensuring that the button gets rerendered any time these fields change.</p>
<p>Finally, <code>ExampleClusterFeatureSettings</code> needs to be connected to the extension, and would typically appear on the cluster <strong>Settings</strong> page via a <code>Renderer.LensExtension.entitySettings</code> entry.
The <code>ExampleExtension</code> would look like this:</p>
<div class="highlight"><pre><span></span><code><span class="k">import</span> <span class="p">{</span> <span class="nx">Common</span><span class="p">,</span> <span class="nx">Renderer</span> <span class="p">}</span> <span class="kr">from</span> <span class="s2">&quot;@k8slens/extensions&quot;</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">ExampleClusterFeatureSettings</span> <span class="p">}</span> <span class="kr">from</span> <span class="s2">&quot;./src/example-cluster-feature-settings&quot;</span>
<span class="k">import</span> <span class="nx">React</span> <span class="kr">from</span> <span class="s2">&quot;react&quot;</span>
<span class="k">export</span> <span class="k">default</span> <span class="kd">class</span> <span class="nx">ExampleExtension</span> <span class="k">extends</span> <span class="nx">Renderer</span><span class="p">.</span><span class="nx">LensExtension</span> <span class="p">{</span>
<span class="nx">entitySettings</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">{</span>
<span class="nx">apiVersions</span><span class="o">:</span> <span class="p">[</span><span class="s2">&quot;entity.k8slens.dev/v1alpha1&quot;</span><span class="p">],</span>
<span class="nx">kind</span><span class="o">:</span> <span class="s2">&quot;KubernetesCluster&quot;</span><span class="p">,</span>
<span class="nx">title</span><span class="o">:</span> <span class="s2">&quot;Example Cluster Feature&quot;</span><span class="p">,</span>
<span class="nx">priority</span>: <span class="kt">5</span><span class="p">,</span>
<span class="nx">components</span><span class="o">:</span> <span class="p">{</span>
<span class="nx">View</span><span class="o">:</span> <span class="p">({</span> <span class="nx">entity</span> <span class="o">=</span> <span class="kc">null</span> <span class="p">}</span><span class="o">:</span> <span class="p">{</span> <span class="nx">entity</span>: <span class="kt">Common.Catalog.KubernetesCluster</span><span class="p">})</span> <span class="p">=&gt;</span> <span class="p">(</span>
<span class="o">&lt;</span><span class="nx">ExampleClusterFeatureSettings</span> <span class="nx">cluster</span><span class="o">=</span><span class="p">{</span><span class="nx">entity</span><span class="p">}</span> <span class="o">/&gt;</span>
<span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">];</span>
<span class="p">}</span>
</code></pre></div>
<p>An entity setting is added to the <code>entitySettings</code> array field of the <code>Renderer.LensExtension</code> class.
Because Lens's catalog can contain different kinds of entities, the kind must be identified.
For more details about the catalog see the <a href="../catalog/">Catalog Guide</a>.
Clusters are a built-in kind, so the <code>apiVersions</code> and <code>kind</code> fields should be set as above.
The <code>title</code> is shown as a navigation item on the cluster <strong>Settings</strong> page and the <code>components.View</code> is displayed when the navigation item is clicked on.
The <code>components.View</code> definition above shows how the <code>ExampleClusterFeatureSettings</code> element is included, and how its <code>cluster</code> prop is set.
<code>priority</code> determines the order of the entity settings, the higher the number the higher in the navigation panel the setting is placed. The default value is 50.</p>
<p>The final result looks like this:</p>
<p><img alt="Cluster Feature Settings" src="../images/clusterfeature.png" /></p>
<p><code>ExampleClusterFeature</code> and <code>ExampleClusterFeatureSettings</code> demonstrate a cluster feature for a simple resource stack.
In practice a resource stack can include many resources, and require more sophisticated life cycle management (upgrades, partial installations, etc.)
Using <code>Renderer.K8sApi.ResourceStack</code> and <code>entitySettings</code> it is possible to implement solutions for more complex cluster features.
The <strong>Lens Metrics</strong> setting (on the cluster <strong>Settings</strong> page) is a good example of an advanced solution.</p>
</article>
</div>
</div>
</main>
<footer class="md-footer">
<nav class="md-footer__inner md-grid" aria-label="Footer">
<a href="../catalog/" class="md-footer__link md-footer__link--prev" aria-label="Previous: Catalog" rel="prev">
<div class="md-footer__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12z"/></svg>
</div>
<div class="md-footer__title">
<div class="md-ellipsis">
<span class="md-footer__direction">
Previous
</span>
Catalog
</div>
</div>
</a>
<a href="../stores/" class="md-footer__link md-footer__link--next" aria-label="Next: Stores" rel="next">
<div class="md-footer__title">
<div class="md-ellipsis">
<span class="md-footer__direction">
Next
</span>
Stores
</div>
</div>
<div class="md-footer__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 11v2h12l-5.5 5.5 1.42 1.42L19.84 12l-7.92-7.92L10.5 5.5 16 11H4z"/></svg>
</div>
</a>
</nav>
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-footer-copyright">
<div class="md-footer-copyright__highlight">
Copyright &copy; 2021 <a href="https://mirantis.com/">Mirantis Inc.</a> - All rights reserved.
</div>
</div>
<div class="md-footer-social">
<a href="https://github.com/lensapp/lens" target="_blank" rel="noopener" title="Lens on GitHub" class="md-footer-social__link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/></svg>
</a>
<a href="https://twitter.com/k8slens" target="_blank" rel="noopener" title="Lens on Twitter" class="md-footer-social__link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"/></svg>
</a>
<a href="http://k8slens.slack.com/" target="_blank" rel="noopener" title="Lens on Slack" class="md-footer-social__link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M94.12 315.1c0 25.9-21.16 47.06-47.06 47.06S0 341 0 315.1c0-25.9 21.16-47.06 47.06-47.06h47.06v47.06zm23.72 0c0-25.9 21.16-47.06 47.06-47.06s47.06 21.16 47.06 47.06v117.84c0 25.9-21.16 47.06-47.06 47.06s-47.06-21.16-47.06-47.06V315.1zm47.06-188.98c-25.9 0-47.06-21.16-47.06-47.06S139 32 164.9 32s47.06 21.16 47.06 47.06v47.06H164.9zm0 23.72c25.9 0 47.06 21.16 47.06 47.06s-21.16 47.06-47.06 47.06H47.06C21.16 243.96 0 222.8 0 196.9s21.16-47.06 47.06-47.06H164.9zm188.98 47.06c0-25.9 21.16-47.06 47.06-47.06 25.9 0 47.06 21.16 47.06 47.06s-21.16 47.06-47.06 47.06h-47.06V196.9zm-23.72 0c0 25.9-21.16 47.06-47.06 47.06-25.9 0-47.06-21.16-47.06-47.06V79.06c0-25.9 21.16-47.06 47.06-47.06 25.9 0 47.06 21.16 47.06 47.06V196.9zM283.1 385.88c25.9 0 47.06 21.16 47.06 47.06 0 25.9-21.16 47.06-47.06 47.06-25.9 0-47.06-21.16-47.06-47.06v-47.06h47.06zm0-23.72c-25.9 0-47.06-21.16-47.06-47.06 0-25.9 21.16-47.06 47.06-47.06h117.84c25.9 0 47.06 21.16 47.06 47.06 0 25.9-21.16 47.06-47.06 47.06H283.1z"/></svg>
</a>
<a href="https://k8slens.dev/" target="_blank" rel="noopener" title="Lens Website" class="md-footer-social__link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M326.612 185.391c59.747 59.809 58.927 155.698.36 214.59-.11.12-.24.25-.36.37l-67.2 67.2c-59.27 59.27-155.699 59.262-214.96 0-59.27-59.26-59.27-155.7 0-214.96l37.106-37.106c9.84-9.84 26.786-3.3 27.294 10.606.648 17.722 3.826 35.527 9.69 52.721 1.986 5.822.567 12.262-3.783 16.612l-13.087 13.087c-28.026 28.026-28.905 73.66-1.155 101.96 28.024 28.579 74.086 28.749 102.325.51l67.2-67.19c28.191-28.191 28.073-73.757 0-101.83-3.701-3.694-7.429-6.564-10.341-8.569a16.037 16.037 0 0 1-6.947-12.606c-.396-10.567 3.348-21.456 11.698-29.806l21.054-21.055c5.521-5.521 14.182-6.199 20.584-1.731a152.482 152.482 0 0 1 20.522 17.197zM467.547 44.449c-59.261-59.262-155.69-59.27-214.96 0l-67.2 67.2c-.12.12-.25.25-.36.37-58.566 58.892-59.387 154.781.36 214.59a152.454 152.454 0 0 0 20.521 17.196c6.402 4.468 15.064 3.789 20.584-1.731l21.054-21.055c8.35-8.35 12.094-19.239 11.698-29.806a16.037 16.037 0 0 0-6.947-12.606c-2.912-2.005-6.64-4.875-10.341-8.569-28.073-28.073-28.191-73.639 0-101.83l67.2-67.19c28.239-28.239 74.3-28.069 102.325.51 27.75 28.3 26.872 73.934-1.155 101.96l-13.087 13.087c-4.35 4.35-5.769 10.79-3.783 16.612 5.864 17.194 9.042 34.999 9.69 52.721.509 13.906 17.454 20.446 27.294 10.606l37.106-37.106c59.271-59.259 59.271-155.699.001-214.959z"/></svg>
</a>
</div>
</div>
</div>
</footer>
</div>
<div class="md-dialog" data-md-component="dialog">
<div class="md-dialog__inner md-typeset"></div>
</div>
<script id="__config" type="application/json">{"base": "../../..", "features": ["toc.autohide", "search.suggest", "search.highlight"], "translations": {"clipboard.copy": "Copy to clipboard", "clipboard.copied": "Copied to clipboard", "search.config.lang": "en", "search.config.pipeline": "trimmer, stopWordFilter", "search.config.separator": "[\\s\\-]+", "search.placeholder": "Search", "search.result.placeholder": "Type to start searching", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.term.missing": "Missing", "select.version.title": "Select version"}, "search": "../../../assets/javascripts/workers/search.df8cae7d.min.js", "version": {"provider": "mike"}}</script>
<script src="../../../assets/javascripts/bundle.82217815.min.js"></script>
</body>
</html>