{"id":1020,"date":"2020-04-13T16:18:32","date_gmt":"2020-04-13T14:18:32","guid":{"rendered":"http:\/\/sematext.solr.pl\/?p=1020"},"modified":"2020-11-14T16:18:55","modified_gmt":"2020-11-14T15:18:55","slug":"solr-8-4-0-plugin-management","status":"publish","type":"post","link":"https:\/\/solr.pl\/en\/2020\/04\/13\/solr-8-4-0-plugin-management\/","title":{"rendered":"Solr 8.4.0 &#8211; Plugin Management"},"content":{"rendered":"\n<p>With the release of Solr 8.4.0 we&#8217;ve got a new functionality that we can use that can help us with extending Solr functionality &#8211; plugin management. Starting with the mentioned version we can install and remove plugins that are downloaded from external plugin repositories. Let&#8217;s see how it works. <\/p>\n\n\n\n<!--more-->\n\n\n\n<h2 class=\"wp-block-heading\">Plugin Management<\/h2>\n\n\n\n<p>With the Solr 8.4.0 we didn&#8217;t only get the script itself but also the whole package of changes such as package management, class loader isolation, artifact read and write API and so on. <\/p>\n\n\n\n<p>Let&#8217;s start from the beginning though. By default Solr comes with the package loading turned off and to be able to use that feature we need to run Solr with the <strong>enable.packges<\/strong> property set to <strong>true<\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ bin\/solr start -c -f -Denable.packages=true<\/pre>\n\n\n\n<p>Now we can start playing around with the packages.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Package Management Basics<\/h2>\n\n\n\n<p>Let&#8217;s try using the <strong>bin\/solr<\/strong> script and type in the following command:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ bin\/solr package <\/pre>\n\n\n\n<p>In result we will get the following response:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">Found 1 Solr nodes:\n \nSolr process 20949 running on port 8983\nPackage Manager\n\n.\/solr package add-repo  \nAdd a repository to Solr.\n\n.\/solr package install [:]\nInstall a package into Solr. This copies over the artifacts from the repository into Solr's internal package store and sets up classloader for this package to be used.\n\n.\/solr package deploy [:] [-y] [--update] -collections &lt;package-name&gt;[:] [-y] [--update] -collections  [-p param1=value1 -p param2=value2 \u2026\nBootstraps a previously installed package into the specified collections. It the package accepts parameters for its setup commands, they can be specified (as per package documentation).\n\n.\/solr package list-installed\nPrint a list of packages installed in Solr.\n\n.\/solr package list-available\nPrint a list of packages available in the repositories.\n\n.\/solr package list-deployed -c \nPrint a list of packages deployed on a given collection.\n\n.\/solr package list-deployed \nPrint a list of collections on which a given package has been deployed.\n\n.\/solr package undeploy  -collections \nUndeploys a package from specified collection(s)\n\nNote: (a) Please add '-solrUrl http:\/\/host:port' parameter if needed (usually on Windows).\n      (b) Please make sure that all Solr nodes are started with '-Denable.packages=true' parameter.<\/pre>\n\n\n\n<p>As you can see the number of options seems to fulfill the needs and the mechanism itself verifies if Solr node is running and it is able to check the installed packages. Let&#8217;s look into that.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Adding New Repository<\/h2>\n\n\n\n<p>To add a new repository we need to provide its name and the URL under which it is available. Of course Solr expects at least a single file to be present in the defined locations &#8211; the <strong>repository.json<\/strong> file. The mentioned file can look as follows (taken from the Solr source code):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">[\n  {\n    \"name\": \"question-answer\",\n    \"description\": \"A natural language question answering plugin\",\n    \"versions\": [\n      {\n        \"version\": \"1.0.0\",\n        \"date\": \"2019-01-01\",\n        \"artifacts\": [\n          {\n            \"url\": \"qarh-1.0.jar\",\n            \"sig\": \"C9UWKkucmY3UNzqn0VLneVMe9kCbJjw7Urc76vGenoRwp32xvNn5ZIGZ7G34xZP7cVjqn\/ltDlLWBZ\/C3eAtuw==\"\n          }\n        ],\n        \"manifest\": {\n          \"version-constraint\": \"8 - 9\",\n          \"plugins\": [\n            {\n              \"name\": \"request-handler\",\n              \"setup-command\": {\n                \"path\": \"\/api\/collections\/${collection}\/config\",\n                \"payload\": {\"add-requesthandler\": {\"name\": \"${RH-HANDLER-PATH}\", \"class\": \"question-answer:fullstory.QARequestHandler\"}},\n                \"method\": \"POST\"\n              },\n              \"uninstall-command\": {\n                \"path\": \"\/api\/collections\/${collection}\/config\",\n                \"payload\": {\"delete-requesthandler\": \"${RH-HANDLER-PATH}\"},\n                \"method\": \"POST\"\n              },\n              \"verify-command\": {\n                \"path\": \"\/api\/collections\/${collection}\/config\/requestHandler?componentName=${RH-HANDLER-PATH}&amp;meta=true\",\n                \"method\": \"GET\",\n                \"condition\": \"$['config'].['requestHandler'].['${RH-HANDLER-PATH}'].['_packageinfo_'].['version']\",\n                \"expected\": \"${package-version}\"\n              }\n            }\n          ],\n          \"parameter-defaults\": {\n            \"RH-HANDLER-PATH\": \"\/mypath\"\n          }\n        }\n      }\n    ]\n  }\n]<\/code><\/pre>\n\n\n\n<p>OK, let&#8217;s try adding the repository then. For that purpose I created a new repository at the <strong>http:\/\/repo.solr.pl<\/strong> using the files that I found in the <a href=\"https:\/\/github.com\/apache\/lucene-solr\/tree\/jira\/solr-13662-3\/solr\/core\/src\/test-files\/solr\/question-answer-repository\">Solr source code<\/a> and I used the following command to add that repository to my local SolrCloud cluster:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">$ bin\/solr package add-repo solrpl http:\/\/repo.solr.pl<\/code><\/pre>\n\n\n\n<p>In my case the operation was successful and I got the following response on my console:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">Added repository: solrpl<\/code><\/pre>\n\n\n\n<p><strong>Warning<\/strong>: in the above example I used a repository that I could access without using SSL. Please don&#8217;t do that in your environment. It is not a good practice &#8211; it is susceptible to the <em>man in the middle<\/em> attacks during which the downloaded files can be replaced.  <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Installing and Removing Packages<\/h2>\n\n\n\n<p>After adding the repository we can list the packages that we can install and use. To list such packages we should run the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">$ bin\/solr package list-available<\/code><\/pre>\n\n\n\n<p>The response to the above command should be similar to the following one:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">Available packages:\n-----\nquestion-answer \t\tA natural language question answering plugin\n\tVersion: 1.0.0<\/code><\/pre>\n\n\n\n<p>One last step before installing the package itself is adding a public key to Solr &#8211; one that will be able to verify the packages downloaded from the added repository. Where to look for such a key? It will be provided to you and I assume that some repositories can also have such. Once we will have the key we can add it to Solr by using the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">$ bin\/solr package add-key publickey.der<\/code><\/pre>\n\n\n\n<p>After all this steps we can finally start installing the packages. It is as simple as running the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">$ bin\/solr package install question-answer:1.0.0<\/code><\/pre>\n\n\n\n<p>The response that I got from Solr was as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">Posting manifest...\nPosting artifacts...\nExecuting Package API to register this package...\nResponse: {\"responseHeader\":{\n    \"status\":0,\n    \"QTime\":66}}\nquestion-answer installed.<\/code><\/pre>\n\n\n\n<p>This means that our package is now ready to be used. We just need to say which  collection or collections should be allowed to use that package. We do that by sending the <em>deploy<\/em> command to Solr, for example by using the following command (I created the <em>test<\/em> collection before running this command):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">$ bin\/solr package deploy question-answer:1.0.0 -collections test<\/code><\/pre>\n\n\n\n<p>The response was as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">Executing {\"add-requesthandler\":{\"name\":\"\/mypath\",\"class\":\"question-answer:fullstory.QARequestHandler\"}} for path:\/api\/collections\/test\/config\nExecute this command (y\/n):\ny\nExecuting http:\/\/localhost:8983\/api\/collections\/test\/config\/requestHandler?componentName=\/mypath&amp;meta=true for collection:test\n{\n  \"responseHeader\":{\n    \"status\":0,\n    \"QTime\":0},\n  \"config\":{\"requestHandler\":{\"\/mypath\":{\n        \"name\":\"\/mypath\",\n        \"class\":\"question-answer:fullstory.QARequestHandler\",\n        \"_packageinfo_\":{\n          \"package\":\"question-answer\",\n          \"version\":\"1.0.0\",\n          \"files\":[\"\/package\/question-answer\/1.0.0\/question-answer-request-handler-1.0.jar\"],\n          \"manifest\":\"\/package\/question-answer\/1.0.0\/manifest.json\",\n          \"manifestSHA512\":\"a91ab5a2c5abd53f0f72c256592c2be8b667cecb8226ac054aeed4d28aac9d743311442f2d58539bb83663a19bd1efb310aaadfd77bea458f3d475161721a114\"}}}}}\n\nActual: 1.0.0, expected: 1.0.0\nDeployed on [test] and verified package: question-answer, version: 1.0.0\nDeployment successful<\/code><\/pre>\n\n\n\n<p>If everything went well and we said that we want to add the new package to our collection we are ready \ud83d\ude42 We can start using it &#8211; for example by using Solr API to create new handlers if the plugin adds them. <\/p>\n\n\n\n<p>When we no longer need a package we just need to <em>undeploy<\/em> it. For example, if we would like to remove the previously deployed package we just need to run the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">$ bin\/solr package undeploy question-answer -collections test<\/code><\/pre>\n\n\n\n<p>In this case the response would be as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">Executing {\"delete-requesthandler\":\"\/mypath\"} for path:\/api\/collections\/test\/config<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">How It Really Works<\/h2>\n\n\n\n<p>The heart of the implementation is related to class path and isolation of the plugins and the core Solr classes. The plugin mechanism assumes that any change in the files that are in the Solr <em>classpath<\/em> requires restart. The rest files can be loaded dynamically and are bound to the configuration stored in Zookeeper. <\/p>\n\n\n\n<p>The basis of the mechanism is a so called <em>Package Store<\/em>. It is a distributed file system which keeps its data on each Solr node in the <em>$SOLR_HOME\/filestore<\/em> directory and each of the files is described by metadata written in a JSON file. Of course, each file stores the checksum in its metadata for verification purposes.  <\/p>\n\n\n\n<p>On top of all of that we have an API allowing us not only to manage the whole packages, but also single files. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The API<\/h2>\n\n\n\n<p>Of course our <strong>bin\/solr<\/strong> tool and installing the packages using it to manage packages is not everything that Solr give us. In addition to that we got the API that allows us to:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>add files using the PUT HTTP method and the <strong>\/api\/cluster\/files\/{file_path}<\/strong> endpoint<\/li><li>retrieve files using the GET HTTP method and the <strong>\/api\/cluster\/files\/{file_path}<\/strong> endpoint<\/li><li>retrieve file metadata using the GET HTTP method and the <strong>\/api\/cluster\/files\/{file_path}?meta=true<\/strong><\/li><li>retrieve files available at a given path using the GET HTTP method using the <strong>\/api\/cluster\/files\/{directory_path}<\/strong> endpoint<\/li><\/ul>\n\n\n\n<p>You should remember that adding a file to Solr is not only about sending it to Solr. You need to sign it using a key that will be available to Solr &#8211; we saw that already. The official Solr documentation has a very good example on how to do that: <a href=\"https:\/\/lucene.apache.org\/solr\/guide\/8_5\/package-manager-internals.html\">https:\/\/lucene.apache.org\/solr\/guide\/8_5\/package-manager-internals.html<\/a>. <\/p>\n\n\n\n<p>Of course managing files is not everything that Solr API allows us. We also have the option to add, remove and download the packages and their versions:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>GET on <strong>\/api\/cluster\/package<\/strong> to download the list of packages<\/li><li>PUT on <strong>\/api\/cluster\/package<\/strong> to add a package<\/li><li>DELETE on <strong>\/api\/cluster\/package<\/strong> to remove a package<\/li><\/ul>\n\n\n\n<p>For example to add a package to Solr we could use a command like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">curl -XPUT 'http:\/\/localhost:8983\/api\/cluster\/package' -H 'Content-type:application\/json' -d  '{\n \"add\": {\n  \"package\" : \"testsolrpl\",\n  \"version\" : \"1.0.0\",\n  \"files\" : [\n   \"\/test\/solrpl\/1.0.0\/testsolrpl.jar\"\n  ]\n }\n}'<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Security<\/h2>\n\n\n\n<p>Having the option to be flexible, being able to use <em>hot-deploy<\/em> and being able to install Solr extensions without the need of brining the whole cluster down carries limitations and security threats. Because of that remember not to add package repositories that you don&#8217;t know. Such repositories can be dangerous and can result in downloading and installing malicious code. The second thing to remember is that you shouldn&#8217;t add repositories that are not using SSL. Adding a repository that is not using SSL exposes you to <em>man in the middle<\/em> attack during which the files can be replaced on the fly leading to installing of the malicious code. And above all of that remember to keep your Solr secure no matter if you use package management or not.  <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p>The functionality of installing Solr extensions without the need of manually downloading them to each node, restarting the nodes and so on is very nice and tempting. Especially to those of us who use such extensions. However, please remember about security and the limitations of the mechanism. If we will be cautious we will have a way of extending Solr in a flexible way. Have fun \ud83d\ude42 <\/p>\n","protected":false},"excerpt":{"rendered":"<p>With the release of Solr 8.4.0 we&#8217;ve got a new functionality that we can use that can help us with extending Solr functionality &#8211; plugin management. Starting with the mentioned version we can install and remove plugins that are downloaded<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","_links_to":"","_links_to_target":""},"categories":[27],"tags":[631,630,164],"class_list":["post-1020","post","type-post","status-publish","format-standard","hentry","category-solr-en","tag-management","tag-plugin-2","tag-solr-2"],"_links":{"self":[{"href":"https:\/\/solr.pl\/en\/wp-json\/wp\/v2\/posts\/1020","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/solr.pl\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/solr.pl\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/solr.pl\/en\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/solr.pl\/en\/wp-json\/wp\/v2\/comments?post=1020"}],"version-history":[{"count":1,"href":"https:\/\/solr.pl\/en\/wp-json\/wp\/v2\/posts\/1020\/revisions"}],"predecessor-version":[{"id":1021,"href":"https:\/\/solr.pl\/en\/wp-json\/wp\/v2\/posts\/1020\/revisions\/1021"}],"wp:attachment":[{"href":"https:\/\/solr.pl\/en\/wp-json\/wp\/v2\/media?parent=1020"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/solr.pl\/en\/wp-json\/wp\/v2\/categories?post=1020"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/solr.pl\/en\/wp-json\/wp\/v2\/tags?post=1020"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}