{"componentChunkName":"component---src-templates-blog-post-js","path":"/2022/03/03/hack-osd/","result":{"data":{"markdownRemark":{"html":"<p>最近接到一个有意思的需求，简单记录一下解决过程。</p>\n<h2 id=\"背景\" style=\"position:relative;\"><a href=\"#%E8%83%8C%E6%99%AF\" aria-label=\"背景 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>背景</h2>\n<p>需求是这样的，我们使用 AWS 的 OpenSearch (即 Elastic 的 Fork 版本) 部署了一套日志服务，用户使用 OpenSearch Dashboards (也即 Kibana 的 Fork 版本) 查看。但我们只想让用户查看日志 (即 Discover 功能)，不想让他们使用 Dashboard / Visualize 等其它功能，界面上需要隐藏这些功能的入口。</p>\n<p>原始界面：</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/f2d931f761b2672fae91b4892306d034/498a4/osd-origin.jpg\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 49.32432432432432%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAEEBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAG9aCSEvF//xAAYEAADAQEAAAAAAAAAAAAAAAAAAxQQQf/aAAgBAQABBQKRJGkkTnT/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAYEAADAQEAAAAAAAAAAAAAAAAAATGRIP/aAAgBAQAGPwKPSPScf//EABwQAAIABwAAAAAAAAAAAAAAAAABEBFBUYHB8f/aAAgBAQABPyGYtw7QSL8lIEf/2gAMAwEAAgADAAAAEF/P/8QAFREBAQAAAAAAAAAAAAAAAAAAAAH/2gAIAQMBAT8QR//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABkQAQEBAQEBAAAAAAAAAAAAAAERACFBUf/aAAgBAQABPxCaIln23fcBxh7VuAjnurxWTAgQ3//Z'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"osd origin\"\n        title=\"osd origin\"\n        src=\"/static/f2d931f761b2672fae91b4892306d034/1c72d/osd-origin.jpg\"\n        srcset=\"/static/f2d931f761b2672fae91b4892306d034/a80bd/osd-origin.jpg 148w,\n/static/f2d931f761b2672fae91b4892306d034/1c91a/osd-origin.jpg 295w,\n/static/f2d931f761b2672fae91b4892306d034/1c72d/osd-origin.jpg 590w,\n/static/f2d931f761b2672fae91b4892306d034/a8a14/osd-origin.jpg 885w,\n/static/f2d931f761b2672fae91b4892306d034/fbd2c/osd-origin.jpg 1180w,\n/static/f2d931f761b2672fae91b4892306d034/498a4/osd-origin.jpg 2168w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span></p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/9300c700723cfe89b7e473700ed0636d/b28e6/osd-origin-2.jpg\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 187.83783783783784%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAmABQDASIAAhEBAxEB/8QAGAABAQADAAAAAAAAAAAAAAAAAAIBBAX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAB2q37TlOqJzi1gCwkH//EABoQAAIDAQEAAAAAAAAAAAAAAAABAhAUESD/2gAIAQEAAQUCxwMcDDDzwdu2f//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQMBAT8BX//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8BX//EAB0QAAEEAgMAAAAAAAAAAAAAAAABEDJBAiAxoeH/2gAIAQEABj8CkpLonkePZbcLt//EAB4QAQACAgEFAAAAAAAAAAAAAAEAETHREEFhgZGx/9oACAEBAAE/IS7RO89Iu6JTfSPHAC7MAO0yl0vwg2XMoArxnP/aAAwDAQACAAMAAAAQD/Q88A//xAAVEQEBAAAAAAAAAAAAAAAAAAABIP/aAAgBAwEBPxAAr//EABURAQEAAAAAAAAAAAAAAAAAABEg/9oACAECAQE/EGv/xAAjEAEAAgEDAgcAAAAAAAAAAAABABEhMUFxEFFhgZGxwfDx/9oACAEBAAE/EBDM9kfxMP2/SIGi28APaNpC3f8AUKdxySiICgC7G8sg+ZMeHECqxmool7JTNLiESGPDpocT/9k='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"osd origin 2\"\n        title=\"osd origin 2\"\n        src=\"/static/9300c700723cfe89b7e473700ed0636d/1c72d/osd-origin-2.jpg\"\n        srcset=\"/static/9300c700723cfe89b7e473700ed0636d/a80bd/osd-origin-2.jpg 148w,\n/static/9300c700723cfe89b7e473700ed0636d/1c91a/osd-origin-2.jpg 295w,\n/static/9300c700723cfe89b7e473700ed0636d/1c72d/osd-origin-2.jpg 590w,\n/static/9300c700723cfe89b7e473700ed0636d/b28e6/osd-origin-2.jpg 626w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span></p>\n<p>预期效果 (也是最终的实现效果)：</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/a8355cb3e98cf1d66fb5334da3b413c8/26ad0/osd-hacked.jpg\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 38.513513513513516%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAdOiQL//xAAYEAADAQEAAAAAAAAAAAAAAAAAAxQRE//aAAgBAQABBQLgnJkk6T//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAXEAADAQAAAAAAAAAAAAAAAAAAATMC/9oACAEBAAY/Aponkmj/xAAZEAACAwEAAAAAAAAAAAAAAAABIQAQkcH/2gAIAQEAAT8hImxoGcZ//9oADAMBAAIAAwAAABD3z//EABYRAQEBAAAAAAAAAAAAAAAAAAABMf/aAAgBAwEBPxCaj//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABkQAQEBAAMAAAAAAAAAAAAAAAERACFBwf/aAAgBAQABPxAAuvQsU8sspwsz/9k='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"osd hacked\"\n        title=\"osd hacked\"\n        src=\"/static/a8355cb3e98cf1d66fb5334da3b413c8/1c72d/osd-hacked.jpg\"\n        srcset=\"/static/a8355cb3e98cf1d66fb5334da3b413c8/a80bd/osd-hacked.jpg 148w,\n/static/a8355cb3e98cf1d66fb5334da3b413c8/1c91a/osd-hacked.jpg 295w,\n/static/a8355cb3e98cf1d66fb5334da3b413c8/1c72d/osd-hacked.jpg 590w,\n/static/a8355cb3e98cf1d66fb5334da3b413c8/a8a14/osd-hacked.jpg 885w,\n/static/a8355cb3e98cf1d66fb5334da3b413c8/fbd2c/osd-hacked.jpg 1180w,\n/static/a8355cb3e98cf1d66fb5334da3b413c8/26ad0/osd-hacked.jpg 2162w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span></p>\n<h2 id=\"调研\" style=\"position:relative;\"><a href=\"#%E8%B0%83%E7%A0%94\" aria-label=\"调研 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>调研</h2>\n<p>简单调研了一下，发现 Kibana 官方提供了关闭相应功能入口的功能 - <a href=\"https://www.elastic.co/guide/en/kibana/current/kibana-privileges.html#_assigning_feature_privileges\">链接</a>，但是很不幸，OpenSearch Dashboard (后面简称 OSD) 还没有加入此功能。</p>\n<p>首先尝试通过 OSD 的 role 进行权限控制，实话说，这玩意太复杂，整不明白，而且即使加上了一些 read only 权限的 role，导航 menu 还会在界面上显示，用户可以进入这些页面，只不过执行一些操作时会提示错误。</p>\n<p>当然，兜底的方案也是有的，毕竟 OSD 的代码也是开源的，我们直接修改源码，把相应的 header 部分移除掉，重新编译部署就行了，但这样我们就得维护一个 fork 版本，虽然维护量极小。</p>\n<p>在浏览器 inspect element 后发现 header 的元素 id 为 \"globalHeaderBars\"，在源码中搜索此关键字，一下子就定位到了源码位置。</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token comment\">// src/core/public/chrome/ui/header/header.tsx</span>\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">function</span> <span class=\"token function\">Header</span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span><span class=\"token operator\">...</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// ...</span>\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>header</span> <span class=\"token attr-name\">className</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>className<span class=\"token punctuation\">}</span></span> <span class=\"token attr-name\">data-test-subj</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>headerGlobalNav<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">id</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>globalHeaderBars<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n          &lt;EuiHeader\n            theme=\"dark\"\n            position=\"fixed\"\n            sections=</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">[</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>直接在 <code class=\"language-text\">return</code> 语句上面加上一行 <code class=\"language-text\">return null;</code> 就行了。</p>\n<p>然后尝试有没有其它更好的解决办法。</p>\n<p>通过搜索发现了一篇官方博客介绍 OSD 的插件机制 - <a href=\"https://opensearch.org/blog/technical-post/2022/01/dashboards-plugins-intro/\">Introduction to OpenSearch Dashboard Plugins</a>，大喜，有了插件机制，那我们就可以为所欲为了。</p>\n<p>看完文章，没完全了解细节，但大致了解了其工作机制。repo 中带了一些 plugin examples，按照 <a href=\"https://github.com/opensearch-project/OpenSearch-Dashboards/blob/main/DEVELOPER_GUIDE.md\">Developer Guide</a> 上的步骤，先把 OSD 在本地以开发模式跑起来。使用 <code class=\"language-text\">yarn start --run-examples</code> 可以加载一些示例 plugins。</p>\n<p>示例 plugins 的源码位置 <code class=\"language-text\">examples</code> 目录中，随便找了一个 plugin，比如 <code class=\"language-text\">developer_examples</code>，在 <code class=\"language-text\">public/plugin.ts</code> 的 <code class=\"language-text\">setup()</code> 生命周期中打印一条 log:</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\">  <span class=\"token keyword\">public</span> <span class=\"token function\">setup</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">core<span class=\"token operator\">:</span> CoreSetup</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'load developer examples plugin'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//...</span>\n  <span class=\"token punctuation\">}</span></code></pre></div>\n<p>刷新网页后发现在 console 中输出中这条 log。</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/354a155d575c29d0397e110efba50db6/4719e/osd-log.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 27.027027027027025%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA80lEQVQY001Qi26EIBD0/z/vkl59nYoiCHq1KiheqtMFk/Y2mczssk8iJjtUogNXPYpWgOuBfIlhXtCOBpl8Qk8L1NeI52LQebYrxEDx8Rv9NIc3ny90j2j8+MR0v8MUJeYkDdjyHEdd4/BcVTiaBgdjOMry8rMMp48JgYPzf0iJCP2AV1Fgo8SdsFGRyx9YCTZNYeI4aM+WBltic7vB0DBTMdgsD7VzksC1LSLjHDJWI2cNSi4CP3iLtlNQcQKpdDivliowp7MUnSq0DmhoK97Rt9GGZtsQ7a8fbCQ8HDW/tMO+rkDdANbishN/dr7pd6P4L9nnd/A/Sk0jAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"osd log\"\n        title=\"osd log\"\n        src=\"/static/354a155d575c29d0397e110efba50db6/fcda8/osd-log.png\"\n        srcset=\"/static/354a155d575c29d0397e110efba50db6/12f09/osd-log.png 148w,\n/static/354a155d575c29d0397e110efba50db6/e4a3f/osd-log.png 295w,\n/static/354a155d575c29d0397e110efba50db6/fcda8/osd-log.png 590w,\n/static/354a155d575c29d0397e110efba50db6/efc66/osd-log.png 885w,\n/static/354a155d575c29d0397e110efba50db6/c83ae/osd-log.png 1180w,\n/static/354a155d575c29d0397e110efba50db6/4719e/osd-log.png 1330w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span></p>\n<p>继续验证，添加移除 header 的逻辑：</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const</span> timeId <span class=\"token operator\">=</span> <span class=\"token function\">setInterval</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> el <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span><span class=\"token function\">getElementById</span><span class=\"token punctuation\">(</span><span class=\"token string\">'globalHeaderBars'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>el<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">clearInterval</span><span class=\"token punctuation\">(</span>timeId<span class=\"token punctuation\">)</span>\n    el<span class=\"token punctuation\">.</span><span class=\"token function\">remove</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token number\">100</span><span class=\"token punctuation\">)</span></code></pre></div>\n<p>刷新网页后发现 header 移除了，但不是很完美，在移除前会闪一下，以及移除后顶部会留下一片空白。</p>\n<p>后来想到了更好的办法，用 MutationObserver。顶部空白则通过修改 body 的 paddingTop 解决。具体实现如下。</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\">  <span class=\"token keyword\">private</span> <span class=\"token function\">removeHeader</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> observer <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">MutationObserver</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">mutations</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'mutations:'</span><span class=\"token punctuation\">,</span> mutations<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n      <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">const</span> mutation <span class=\"token keyword\">of</span> mutations<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">let</span> i <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span> i <span class=\"token operator\">&lt;</span> mutation<span class=\"token punctuation\">.</span>addedNodes<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">;</span> i<span class=\"token operator\">++</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">const</span> node <span class=\"token operator\">=</span> mutation<span class=\"token punctuation\">.</span>addedNodes<span class=\"token punctuation\">[</span>i<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span><span class=\"token punctuation\">(</span>node <span class=\"token keyword\">instanceof</span> <span class=\"token class-name\">HTMLElement</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">continue</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>node<span class=\"token punctuation\">.</span>nodeName <span class=\"token operator\">===</span> <span class=\"token string\">'HEADER'</span> <span class=\"token operator\">&amp;&amp;</span> node<span class=\"token punctuation\">.</span>classList<span class=\"token punctuation\">.</span><span class=\"token function\">contains</span><span class=\"token punctuation\">(</span><span class=\"token string\">'headerGlobalNav'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            node<span class=\"token punctuation\">.</span>firstChild<span class=\"token operator\">?.</span><span class=\"token function\">remove</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            document<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">.</span>style<span class=\"token punctuation\">.</span>paddingTop <span class=\"token operator\">=</span> <span class=\"token string\">'10px'</span><span class=\"token punctuation\">;</span>\n            observer<span class=\"token punctuation\">.</span><span class=\"token function\">disconnect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    observer<span class=\"token punctuation\">.</span><span class=\"token function\">observe</span><span class=\"token punctuation\">(</span>document<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> childList<span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> subtree<span class=\"token operator\">:</span> <span class=\"token boolean\">true</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">public</span> <span class=\"token function\">setup</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">core<span class=\"token operator\">:</span> CoreSetup</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'load developer examples plugin'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">removeHeader</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//...</span>\n  <span class=\"token punctuation\">}</span></code></pre></div>\n<p>通过上面的试验验证了插件的可行性。</p>\n<p>但此时我们是直接修改了 repo 示例 plugin 的源码。接下来我们要创建一个新的插件并能进行发布，供 OSD 使用。</p>\n<h2 id=\"开发-osd-插件\" style=\"position:relative;\"><a href=\"#%E5%BC%80%E5%8F%91-osd-%E6%8F%92%E4%BB%B6\" aria-label=\"开发 osd 插件 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>开发 OSD 插件</h2>\n<p>插件的开发要在 OSD 的 repo 中进行。</p>\n<p>在 OSD 源码根目录下执行 <code class=\"language-text\">node scripts/generate_plugin.js hide_header</code> 生成一个默认的插件。它会问你插件要放到哪，如果是要放到 repo 里，则插件会放到 src/plugins 目录下，否则放到 plugins 目录中。plugins 目录被 git 忽略了，因此不会纳入版本控制。</p>\n<p>执行 <code class=\"language-text\">yarn start</code> 启动 OSD，发现该插件已加载，显示在左侧导航菜单。</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/2e7976c4f540037505b3526197d5f3a3/f058b/osd-menu.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 67.56756756756756%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAABxElEQVQ4y4VU227aQBD1t/chv9CH/kSi9KqkTURLW3KTmqRRSiGEcLEDNGAbexd7d09npkAp3ZCRjjz27p49c2bkABTOAdZBws2xCF4rjX0SvG/0a4yg3e3hfjiEUgphFCFNUyRJIojjWN6dW73i3+C1sjSS98J7BIe1Y5x+v8JoOMKr129xcFjB9s4u9vY/4N3ee3w7v4C1dnl4FRzGGLo8lbwfDRCwVjc/sCl8Kte/CeF4EmOa5ZhMEjyMJ8hyJYusaoHFwVWC1W923gAh7PYj9MOBGHpz2wFf4FOyJNugVAhns4JMLVEUBThnaD2D0ho5qZ1RLoQeD9f9FMLbdhfX9SY63RD1Rktwdd1A/UdT8ogMd48o8yr0bbA8V4t5vLiE/dmAWfrq/oMxdj42RDilsnKlBYrK4zIVb3r5BubZFvT+ATQ1TRclNNvhAZ+zpFIUHp+do1KtofrlBJVPNXz8fEQDPkDx/AWy6lckdHOSKSTT/M/TgzjNUdBwh9EQQZZrpLQ5JRVJmkmuFDWFiBia1h9T5lVY0qSzB2bNH0MllgSfZxs97PRCGeYWzWD7rgfueuOmLZ1utu6olBJ/fxtPd/k3yYo6hg6l3QIAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"osd menu\"\n        title=\"osd menu\"\n        src=\"/static/2e7976c4f540037505b3526197d5f3a3/fcda8/osd-menu.png\"\n        srcset=\"/static/2e7976c4f540037505b3526197d5f3a3/12f09/osd-menu.png 148w,\n/static/2e7976c4f540037505b3526197d5f3a3/e4a3f/osd-menu.png 295w,\n/static/2e7976c4f540037505b3526197d5f3a3/fcda8/osd-menu.png 590w,\n/static/2e7976c4f540037505b3526197d5f3a3/f058b/osd-menu.png 630w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span></p>\n<p>如果不想让它显示在左侧导航菜单，修改 <code class=\"language-text\">core.application.register()</code>，添加 <code class=\"language-text\">navLinkStatus: AppNavLinkStatus.hidden</code> 的选项。</p>\n<div class=\"gatsby-highlight\" data-language=\"diff\"><pre class=\"language-diff\"><code class=\"language-diff\"><span class=\"token unchanged\"><span class=\"token prefix unchanged\"> </span>   core.application.register({\n<span class=\"token prefix unchanged\"> </span>     id: 'hideHeader',\n<span class=\"token prefix unchanged\"> </span>     title: PLUGIN_NAME,\n</span><span class=\"token inserted-sign inserted\"><span class=\"token prefix inserted\">+</span>     navLinkStatus: AppNavLinkStatus.hidden,</span></code></pre></div>\n<p>然后添加 removeHeader() 的逻辑。</p>\n<div class=\"gatsby-highlight\" data-language=\"ts\"><pre class=\"language-ts\"><code class=\"language-ts\"><span class=\"token comment\">// plugins/hide_header/public/plugin.ts</span>\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">HideHeaderPlugin</span>\n  <span class=\"token keyword\">implements</span> <span class=\"token class-name\">Plugin<span class=\"token operator\">&lt;</span>HideHeaderPluginSetup<span class=\"token punctuation\">,</span> HideHeaderPluginStart<span class=\"token operator\">></span></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">private</span> <span class=\"token function\">removeHeader</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> observer <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">MutationObserver</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>mutations<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      <span class=\"token builtin\">console</span><span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'mutations:'</span><span class=\"token punctuation\">,</span> mutations<span class=\"token punctuation\">)</span>\n\n      <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">const</span> mutation <span class=\"token keyword\">of</span> mutations<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">let</span> i <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span> i <span class=\"token operator\">&lt;</span> mutation<span class=\"token punctuation\">.</span>addedNodes<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">;</span> i<span class=\"token operator\">++</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">const</span> node <span class=\"token operator\">=</span> mutation<span class=\"token punctuation\">.</span>addedNodes<span class=\"token punctuation\">[</span>i<span class=\"token punctuation\">]</span>\n          <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span><span class=\"token punctuation\">(</span>node <span class=\"token keyword\">instanceof</span> <span class=\"token class-name\">HTMLElement</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">continue</span>\n          <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>\n            node<span class=\"token punctuation\">.</span>nodeName <span class=\"token operator\">===</span> <span class=\"token string\">'HEADER'</span> <span class=\"token operator\">&amp;&amp;</span>\n            node<span class=\"token punctuation\">.</span>classList<span class=\"token punctuation\">.</span><span class=\"token function\">contains</span><span class=\"token punctuation\">(</span><span class=\"token string\">'headerGlobalNav'</span><span class=\"token punctuation\">)</span>\n          <span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            node<span class=\"token punctuation\">.</span>firstChild<span class=\"token operator\">?.</span><span class=\"token function\">remove</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n            document<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">.</span>style<span class=\"token punctuation\">.</span>paddingTop <span class=\"token operator\">=</span> <span class=\"token string\">'10px'</span>\n            observer<span class=\"token punctuation\">.</span><span class=\"token function\">disconnect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n            <span class=\"token keyword\">break</span>\n          <span class=\"token punctuation\">}</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    observer<span class=\"token punctuation\">.</span><span class=\"token function\">observe</span><span class=\"token punctuation\">(</span>document<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> childList<span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> subtree<span class=\"token operator\">:</span> <span class=\"token boolean\">true</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">public</span> <span class=\"token function\">setup</span><span class=\"token punctuation\">(</span>core<span class=\"token operator\">:</span> CoreSetup<span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> HideHeaderPluginSetup <span class=\"token punctuation\">{</span>\n    <span class=\"token builtin\">console</span><span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'load hide header plugin'</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">removeHeader</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n    <span class=\"token comment\">//...</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token comment\">//...</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>其实这个插件我们需要的就只有这一块逻辑，其它的都不需要，完全可以裁剪掉 (懒得搞了)。</p>\n<h2 id=\"发布插件\" style=\"position:relative;\"><a href=\"#%E5%8F%91%E5%B8%83%E6%8F%92%E4%BB%B6\" aria-label=\"发布插件 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>发布插件</h2>\n<p>在此插件的根目录下，执行 <code class=\"language-text\">yarn build</code>，会询问要将此插件用于哪个版本的 OSD，填入对应的版本就行，比如我们布署的是 1.2.0，那就填 1.2.0。如果版本不匹配，OSD 会加载失败。</p>\n<p>然后在 build 目录下就会生成一个 zip 包，比如 <code class=\"language-text\">hideHeader-1.2.0.zip</code>。</p>\n<p>把这个 zip 包拷贝到生产环境上的 OSD 二进制所在目录的 plugins 目录，解压即可。</p>\n<p>然后重启 OSD，就可以看到效果了。</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">opensearch-dashboards-1.2.0-linux-x64$ ls\nLICENSE.txt  NOTICE.txt  README.txt  bin  config  data  manifest.yml  node  node_modules  package.json  plugins  src\n\nopensearch-dashboards-1.2.0-linux-x64$ cd plugins/\nopensearch-dashboards-1.2.0-linux-x64/plugins$ ls\nalertingDashboards          ganttChartDashboards  indexManagementDashboards  queryWorkbenchDashboards  securityDashboards\nanomalyDetectionDashboards  hideHeader-1.2.0.zip  observabilityDashboards    reportsDashboards\n\nopensearch-dashboards-1.2.0-linux-x64/plugins$ unzip hideHeader-1.2.0.zip</code></pre></div>\n<h2 id=\"repo\" style=\"position:relative;\"><a href=\"#repo\" aria-label=\"repo permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Repo</h2>\n<ul>\n<li><a href=\"https://github.com/baurine/hide-osd-header\">hide-osd-header</a></li>\n</ul>","frontmatter":{"title":"隐藏 OpenSearch Dashboards 导航","date":"March 03, 2022"}}},"pageContext":{"slug":"/2022/03/03/hack-osd/"}},"staticQueryHashes":["3159585216"]}