<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: HS</title>
    <description>The latest articles on Forem by HS (@_hs_).</description>
    <link>https://forem.com/_hs_</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F87385%2F1843b0bd-fb45-4f43-9f48-a83020ae7952.jpg</url>
      <title>Forem: HS</title>
      <link>https://forem.com/_hs_</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/_hs_"/>
    <language>en</language>
    <item>
      <title>Micronaut Pulsar</title>
      <dc:creator>HS</dc:creator>
      <pubDate>Sat, 11 Dec 2021 20:32:36 +0000</pubDate>
      <link>https://forem.com/_hs_/micronaut-pulsar-6bo</link>
      <guid>https://forem.com/_hs_/micronaut-pulsar-6bo</guid>
      <description>&lt;p&gt;A while ago I worked with Apache Pulsar and had interest in Micronaut. At the time there was no Micronaut Pulsar integration so I decided to give it a try. It was fun to go through certain Micronaut things and understand how and what to do but more than that I finally could contribute to open source project. Graeme Rocher is quite a nice guy I have to say. I expected more problems regarding coding style or something but it turns out if the tools pass the test you're quite OK with the contribution.&lt;/p&gt;

&lt;p&gt;So to try to make it a bit more public I decided to publish a simple blog post about it. You can find the repository &lt;a href="https://github.com/micronaut-projects/micronaut-pulsar" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does it do?
&lt;/h2&gt;

&lt;p&gt;It just makes integration between Micronaut framework and Apache Pulsar library. In the background it still uses Apache Pulsar client library for Java. Yes, that means it does still have some reflection usage in the background but I figured it's better to use existing piece of code than to rewrite whole protocol implementation in Java. I did test it against native image using Graal tools so I did some effort into making Pulsar client Java library work with it. It's not the best case scenario but it works so I guess it's good enough until either their library is rewritten to not use reflections or help is provided with rewriting this implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's supported?
&lt;/h2&gt;

&lt;p&gt;Just the "basics" so to speak. Configuring Apache Pulsar client with TLS, OAuth2, or just setting the local URL. Consumers and producers are the main focus so one could easily annotate classes/interfaces and their methods to consume/send messages. I did replace Pulsars object mapper with Micronaut one so you can rely on the same processing when using JSON, and thanks to &lt;a href="https://github.com/yawkat" rel="noopener noreferrer"&gt;Johnas Conrad&lt;/a&gt; it's now replaced with new JsonMapper instead of ObjectMapper.&lt;/p&gt;

&lt;p&gt;Annotations are made for consumers and producers but readers can have just annotation for injections - they will be automatically created for each injection point.&lt;/p&gt;

&lt;p&gt;Protobuf mapping is not tested so the whole module is treated as snapshot - beta. If someone would like to give it a go feel free to fork and play with it. Other than that String, byte, JSON are well tested and working even Kotlin should be fine with suspend marked methods.&lt;/p&gt;

&lt;p&gt;I assume anyone interested in this module understand Pulsars approach so I will not detail types of consumers or other things. I will just mention that most of the things are configurable through annotation properties like type of subscription, single topic, regex topic, acknowledgment timeout, and such. &lt;/p&gt;

&lt;h2&gt;
  
  
  Plans for the future
&lt;/h2&gt;

&lt;p&gt;I wanted to build multi-tenant module which would allow reader, consumer, and producer injection point to detect tenant in context and instantiate or reuse existing one. This however would assume that producers and consumers that are made with annotations require a list in application properties or someplace to boot those consumers and readers during runtime. Otherwise it's not possible given that tenant headers are passed in requests. This would mean that consumers (or producers) would not be instantiated until request is made that would make that tenant known to the application. I think this is not usable so didn't implement it but let me know if you think otherwise.&lt;/p&gt;

&lt;p&gt;Because module is in testing phase and I have no idea would be it be used enough I'd rather wait for requests and see where should effort be put it than to make code for something that is not used at all.&lt;/p&gt;

&lt;p&gt;So, I'm hoping that this post would make it a bit more public and check interest in this module. If there's any I can get some feedback or maybe even couple of pull requests :D.&lt;/p&gt;

&lt;p&gt;Given that I don't use Pulsar or Micronaut in my daily job anymore I don't have too much insights on what could be useful or what is bad so please do feel free to make suggestions or any kind of input.&lt;/p&gt;

</description>
      <category>java</category>
      <category>micronaut</category>
      <category>pulsar</category>
      <category>apachepulsar</category>
    </item>
    <item>
      <title>Promotion bots on dev.to</title>
      <dc:creator>HS</dc:creator>
      <pubDate>Mon, 25 Jan 2021 21:20:54 +0000</pubDate>
      <link>https://forem.com/_hs_/promotion-bots-on-dev-to-4dg2</link>
      <guid>https://forem.com/_hs_/promotion-bots-on-dev-to-4dg2</guid>
      <description>&lt;p&gt;Have some "members" abused the good nature of this community for allowing "Read more"? I can constantly see generic names like jane doe, john, bill... posting parts of blog posts and putting Read More at the end of it. It's probably the same person but I just started tagging and reporting them. If anyone spots this feel free to flag to admins as it's clearly a some sort of promotion bot "phishing" users to it's own page.&lt;/p&gt;

&lt;p&gt;Update: just for fun I followed the link. It looks like dev.to got a makeover (not necessarily in a good way). I think that it's basically a rip-off so maybe someone just took Forem, customized it and now trying to push users there. Some people just want to watch the world burn.&lt;/p&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>Apache Pulsar OAuth2 dev setup with Docker and KeyCloak</title>
      <dc:creator>HS</dc:creator>
      <pubDate>Mon, 28 Dec 2020 00:35:41 +0000</pubDate>
      <link>https://forem.com/_hs_/apache-pulsar-oauth2-dev-setup-with-docker-and-keycloak-k64</link>
      <guid>https://forem.com/_hs_/apache-pulsar-oauth2-dev-setup-with-docker-and-keycloak-k64</guid>
      <description>&lt;p&gt;So I've been eager to use something easier to set up than generating those TLS certificates and such for development environment, yet still somewhat more secure than just hardcoded JWT inside a file in Pulsar VM (container in this case). If you're not familiar with JWT setup for Pulsar, it's not necessary, but you can think of it as lesser OAuth2 since they both rely mainly on verifying JWT but process for logging in with Brokers and Clients can be different.&lt;/p&gt;

&lt;h1&gt;
  
  
  KeyCloak setup
&lt;/h1&gt;

&lt;p&gt;KeyCloak requires only client app to be set up for this. App needs to Service Accounts Enabled to be on which is basically &lt;em&gt;client_credentials&lt;/em&gt; grant type in OAuth2. In order to see this option &lt;em&gt;Access Type&lt;/em&gt; has to be set to &lt;em&gt;"confidential"&lt;/em&gt;.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fe3ci70vwpbpxom1dvsnv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fe3ci70vwpbpxom1dvsnv.png" alt="Alt Text" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next thing to set it the "specialised" claim. This claim would be easy to set as &lt;em&gt;realm role&lt;/em&gt; or &lt;em&gt;client role&lt;/em&gt; but Apache Pulsar has issues reading complex types in JWT by default, and so to skip writing custom authentication resolver we must enforce claim inside JWT to be simple string. Problem is that role claims by default go into array even if specified as non-multivalued. So one could get something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[admin]"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;or&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but we need&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;for Pulsar to play nice with defaults.&lt;br&gt;
which is unexpected given that it should be only 1 string. To skip this hassle go and set-up &lt;em&gt;Hardcoded claim&lt;/em&gt;.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fme3uafx57s6dhbamfmok.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fme3uafx57s6dhbamfmok.png" alt="Alt Text" width="800" height="92"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjb1kguoinpvjdrhc4nsx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjb1kguoinpvjdrhc4nsx.png" alt="Alt Text" width="800" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Token claim name&lt;/strong&gt; can be &lt;strong&gt;&lt;em&gt;role&lt;/em&gt;&lt;/strong&gt; and value can be for this purpose &lt;strong&gt;superuser&lt;/strong&gt;. These values are important for Pulsar setup so keep track of what was put in here. Pulsar will require you to set up role name for the &lt;em&gt;superuser&lt;/em&gt; which is dedicated role for managing all Pulsar stuff. On the other hand other roles might be used but then you need to remember to &lt;em&gt;setup tenant namespaces to be accessible by that role&lt;/em&gt;. This means one would have to manage that pulsar instance manually through REST or CLI or something else to setup tenant namespaces manageable by user role you wish to use by certain app.&lt;/p&gt;

&lt;p&gt;Next, get the public key of the realm. Easy you might thing? Well no, again because of Pulsar. Go to &lt;em&gt;Realm Settings&lt;/em&gt; and into &lt;em&gt;Keys&lt;/em&gt; tab. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fstf966kovcqst01r0458.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fstf966kovcqst01r0458.png" alt="Alt Text" width="550" height="158"&gt;&lt;/a&gt;&lt;br&gt;
There you should find &lt;em&gt;RS256&lt;/em&gt; under active tab if all defaults were left as-is. &lt;strong&gt;If you wish to use different algorithm please also mark that as important and keep track of it as well as of the key value&lt;/strong&gt;. If not then please continue and click on &lt;em&gt;Public Key&lt;/em&gt; of that RS256 row.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwqrhsn4mt4ct5zmbwtbk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwqrhsn4mt4ct5zmbwtbk.png" alt="Alt Text" width="800" height="20"&gt;&lt;/a&gt;&lt;br&gt;
You should get some Base64 text. It will look like gibberish of alphanumeric and some special characters. &lt;/p&gt;

&lt;p&gt;Now copy that value and somehow &lt;strong&gt;convert it to bytes and store as file&lt;/strong&gt;. Pulsar lib for java at 2.7 will try to read it as x509 which will fail as it needs to be decoded into bytes prior to this. Steps to do it are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Copy base64 value&lt;/li&gt;
&lt;li&gt;Decode it back to bytes using either something online or simply program your own mini script&lt;/li&gt;
&lt;li&gt;Store bytes into file
A Groovy example:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"MACakje21/adkjwp9qmk4231/ea\d;qwdq=="&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="na"&gt;decodeBase64&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"yourfilename"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Why Groovy? Well it was fast to write and has all those &lt;code&gt;.decode&lt;/code&gt; and &lt;code&gt;.bytes&lt;/code&gt; and... you can execute it as script through IntelliJ - Tools -&amp;gt; Groovy Console. I was mainly using JVM things with pulsar and Python but Python fails to use KeyCloak settings because of some previous bug which was fixed for Java and C++ but apparently not for Python which should rely on C++ lib.&lt;/p&gt;
&lt;h1&gt;
  
  
  Pulsar image
&lt;/h1&gt;

&lt;p&gt;After setting up KeyCloak and storing that key to some file we can build a customised image that uses OAuth2 in &lt;strong&gt;standalone&lt;/strong&gt; mode.&lt;/p&gt;

&lt;p&gt;Now create a file that you will later copy into image. Please visit &lt;a href="https://pulsar.apache.org/docs/en/security-oauth2/" rel="noopener noreferrer"&gt;Pulsar docs&lt;/a&gt; if you want more info. If not then just treat this as credentials file for OAuth2 client app so client ID and secret from KeyCloak (go to App then first tab contains ID and Credentials tab has the secret). Below is simple example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"client_credentials"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"client_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pulsar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"client_secret"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dadada7a7a7-a7ad77ad7da-da7a7ad7ad77"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"issuer_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://host.docker.internal/auth/realms/test"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I named it &lt;strong&gt;oauth2.json&lt;/strong&gt;. _Download files conf/standalone.conf and conf/client.conf from &lt;a href="https://github.com/apache/pulsar/tree/master/conf" rel="noopener noreferrer"&gt;Pulsar repository&lt;/a&gt;. These files will be changed to configure Pulsar image to use Authentication.&lt;/p&gt;

&lt;p&gt;Configure &lt;strong&gt;standalone.conf&lt;/strong&gt;. Set properties in that file to something like in below example@&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;authenticationProviders&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;org.apache.pulsar.broker.authentication.AuthenticationProviderToken&lt;/span&gt;
&lt;span class="c"&gt;# here goes the role you've picked in case you want KeyCloak client app to behave in superuser or else just leave default
&lt;/span&gt;&lt;span class="py"&gt;superUserRoles&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;superuser&lt;/span&gt;

&lt;span class="c"&gt;#brokers need to login to other things so they must also be set up
&lt;/span&gt;&lt;span class="py"&gt;brokerClientAuthenticationPlugin&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;org.apache.pulsar.client.impl.auth.oauth2.AuthenticationOAuth2&lt;/span&gt;
&lt;span class="c"&gt;#oauth2.json path should be set to wherever Dockerfile is copying it
&lt;/span&gt;&lt;span class="py"&gt;brokerClientAuthenticationParameters&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;{"issuerUrl": "https://host.docker.internal/auth/realms/test","privateKey": "/pulsar/oauth2.json","audience": "pulsar"}&lt;/span&gt;
&lt;span class="c"&gt;#bytes from base64, file path must be as defined by Dockerfile
&lt;/span&gt;&lt;span class="py"&gt;tokenPublicKey&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;file:///pulsar/oauth_public.key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will tell brokers to use JSON passed as a parameter, extract file under &lt;strong&gt;privateKey&lt;/strong&gt; and post it's content as payload to &lt;strong&gt;isssuerUrl&lt;/strong&gt;. Thus generating the JWT and refreshing when necessary. &lt;strong&gt;&lt;em&gt;brokerClientAuthenticationPlugin&lt;/em&gt;&lt;/strong&gt; is different from the &lt;strong&gt;authenticationProviders&lt;/strong&gt; because broker generates JWT every now and then by authenticating to KeyCloak while main provider will only verify that token is valid (non-expired and signature is good). &lt;strong&gt;&lt;em&gt;tokenPublicKey&lt;/em&gt;&lt;/strong&gt; will instruct which file to use to verify JWT signature. It's the same as for simple JWT setup without the OAuth2. What's different here is that we need public key from the server while JWT can have key pair generated for Pulsar only and used by it or symmetric key for the same purpose. OAuth2 should not share it's private keys thus symmetric key is not a good option. KeyCloak by default uses asymmetric so it helps quite a bit.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Alternatively, if KeyCloak is set to never expire tokens, one can be generated in advance for Pulsar brokers and then copied into Pulsar container. This could be used instead of AuthenticationOAuth2 and parameters for broker would be file path to JWT token file.&lt;/em&gt; I would not advice alternative approach as it forces you to use never-expiring tokens on all of your realm not just that particular app.&lt;/p&gt;

&lt;p&gt;Don't forget to configure your CLI tools. These are needed to run, add tenants/namespaces..., test topics, and do any kind of administration through CLI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;authPlugin&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;org.apache.pulsar.client.impl.auth.oauth2.AuthenticationOAuth2&lt;/span&gt;

&lt;span class="py"&gt;authParams&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;{"issuerUrl": "https://host.docker.internal/auth/realms/test","privateKey": "/pulsar/oauth2.json","audience": "pulsar"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So same values are used as for broker configuration plugin and params.&lt;/p&gt;

&lt;p&gt;And finally Dockerfile. I made something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; apachepulsar/pulsar-standalone:2.7.0&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /pulsar&lt;/span&gt;
&lt;span class="c"&gt;#using workdir /pulsar we copy public key and it's path will be /pulsar/oauth_public.key and this value must be same as in properties for standalone&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; oauth_public.key oauth_public.key&lt;/span&gt;
&lt;span class="c"&gt;#note oauth2.json is being copied from into workdir /pulsar which is linked in brokerClientAuthenticationParameters&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; oauth2.json oauth2.json&lt;/span&gt;
&lt;span class="c"&gt;#client for CLI tools&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; client.conf conf/client.conf&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; standalone.conf conf/standalone.conf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you used same naming then you should be good to go with this image. One note is that I'm relying on &lt;strong&gt;-standalone&lt;/strong&gt; instead of just &lt;em&gt;pulsar&lt;/em&gt; or &lt;em&gt;pulsar-all&lt;/em&gt;. If you choose something else you might want to add start-up command at the end to run pulsar when you trigger container run from docker. Standalone version runs automatically but others I used didn't so optionally use at the end&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; [ "/pulsar/bin/pulsar", "standalone"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;if you used other image as a base.&lt;/p&gt;

&lt;h1&gt;
  
  
  Build image and run
&lt;/h1&gt;

&lt;p&gt;This should be sufficient to run it. I made something similar and tested it with CLI, Java, and Python clients where I've noticed that Python complains about &lt;code&gt;.well-known/...&lt;/code&gt; which was exception I got when Java library was not fixed to respect path of Issuer URL. What was wrong with Java lib is that URL was stripped of path and only root part was used so if you have &lt;a href="http://keycloak/realms/test" rel="noopener noreferrer"&gt;http://keycloak/realms/test&lt;/a&gt; it would only take &lt;a href="http://keycloak/" rel="noopener noreferrer"&gt;http://keycloak/&lt;/a&gt; so keep that in mind while testing.&lt;/p&gt;

&lt;p&gt;Hope it goes well.&lt;/p&gt;

</description>
      <category>pulsar</category>
      <category>docker</category>
      <category>keycloak</category>
      <category>oauth2</category>
    </item>
    <item>
      <title>Automate Micronaut 2.1 Docker build with Azure DevOps / Pipelines</title>
      <dc:creator>HS</dc:creator>
      <pubDate>Mon, 26 Oct 2020 20:16:14 +0000</pubDate>
      <link>https://forem.com/_hs_/automate-micronaut-2-1-docker-build-with-azure-devops-pipelines-5b6m</link>
      <guid>https://forem.com/_hs_/automate-micronaut-2-1-docker-build-with-azure-devops-pipelines-5b6m</guid>
      <description>&lt;p&gt;Recently Micronaut 2.1 was published with yet another feature that makes us "end-developers" work even less. It's called "Gradle Docker Plugin" and you can check it out &lt;a href="https://github.com/bmuschko/gradle-docker-plugin" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Micronaut has adopted this feature and Graeme (creator of Micronaut) posted &lt;a href="https://www.youtube.com/watch?v=4QnuVlEDprs" rel="noopener noreferrer"&gt;this YouTube video&lt;/a&gt; explaining some features including how to use this Gradle plugin with Micronaut.&lt;/p&gt;

&lt;h1&gt;
  
  
  Prerequisites
&lt;/h1&gt;

&lt;p&gt;I assume that you've setup Container Registry in Azure and have all connections set up properly to communicate between Azure DevOps and registry.&lt;br&gt;
Also I hope you have a project with Micronaut 2.1 or greater version because this feature is only available starting from 2.1.0&lt;/p&gt;
&lt;h1&gt;
  
  
  Pipeline setup
&lt;/h1&gt;

&lt;p&gt;My first reaction to this was "nice" and then I thought "Well how can use this to tag images with other than &lt;em&gt;latest&lt;/em&gt; default tag while using Azure Pipelines". Thing is that in order to make a custom tag I did something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gradle"&gt;&lt;code&gt;&lt;span class="n"&gt;dockerBuild&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;images&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"myproject:$project.version"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"myproject:latest"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but &lt;em&gt;azure-pipelines.yaml&lt;/em&gt; will contain by default something like this as a task for deploying&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Docker@2&lt;/span&gt;
    &lt;span class="s"&gt;displayName&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and push an image to container registry&lt;/span&gt;
      &lt;span class="s"&gt;inputs&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;buildAndPush&lt;/span&gt;
        &lt;span class="na"&gt;repository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(imageRepository)&lt;/span&gt;
        &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(dockerfilePath)&lt;/span&gt;
        &lt;span class="na"&gt;containerRegistry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(dockerRegistryServiceConnection)&lt;/span&gt;
        &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;$(tag)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but that &lt;strong&gt;$(tag)&lt;/strong&gt; variable is hard-coded to read from something like '$(Build.BuildId)' which is not something very useful to tag your own images with because you probably want to track your own versions. Top of your pipeline yaml should have something like below config&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Container registry service connection established during pipeline creation&lt;/span&gt;
  &lt;span class="na"&gt;dockerRegistryServiceConnection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;some-uuid-some-uuid'&lt;/span&gt;
  &lt;span class="na"&gt;imageRepository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;yourAzureImageRepository'&lt;/span&gt;
  &lt;span class="na"&gt;containerRegistry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;yourAzureIMageRepoistory.azurecr.io'&lt;/span&gt;
  &lt;span class="na"&gt;dockerfilePath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$(Build.SourcesDirectory)'&lt;/span&gt;
  &lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$(Build.BuildId)'&lt;/span&gt;
  &lt;span class="c1"&gt;# Agent VM image name&lt;/span&gt;
  &lt;span class="na"&gt;vmImageName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ubuntu-latest'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can remove that &lt;strong&gt;tag: ...&lt;/strong&gt; part, as we are trying to put our own info in there, and the &lt;strong&gt;dockerfilePath&lt;/strong&gt; as we will build image with gradle so we don't need the Dockerfile.&lt;/p&gt;

&lt;p&gt;Instead let's make a script that will extract version from our &lt;em&gt;build.gradle&lt;/em&gt; file. Add a &lt;em&gt;bash&lt;/em&gt; task &lt;strong&gt;before&lt;/strong&gt; task &lt;em&gt;buildDocker&lt;/em&gt; task. Something like this should give you version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'^version[[:space:]]*"\(.\)\+"$'&lt;/span&gt; build.gradle | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"##vso[task.setvariable variable=version]&lt;/span&gt;&lt;span class="nv"&gt;$version&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then your task may look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bash@3&lt;/span&gt;
    &lt;span class="s"&gt;inputs&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;targetType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;inline'&lt;/span&gt;
      &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;#!/usr/bin/env bash&lt;/span&gt;
        &lt;span class="s"&gt;version=$(grep '^version[[:space:]]*"\(.\)\+"$' build.gradle | awk '{print $2} | sed 's/"//g'')&lt;/span&gt;
        &lt;span class="s"&gt;echo "##vso[task.setvariable variable=version]$version"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Windows pipelines?
&lt;/h3&gt;

&lt;p&gt;I made a script which should give you same result when your running your pipelines on Windows platform&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$versionVal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;\build.gradle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select-String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Pattern&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'^version\s*\"(?&amp;lt;version&amp;gt;.+)\"$'&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Matches&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Groups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"##vso[task.setvariable variable=version]&lt;/span&gt;&lt;span class="nv"&gt;$version&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm not 100% sure that it works I just tested it in PowerShell and I got the version number printed out so I guess it works :D.&lt;/p&gt;

&lt;h3&gt;
  
  
  Maven?
&lt;/h3&gt;

&lt;p&gt;Well just check &lt;a href="https://stackoverflow.com/a/56039297" rel="noopener noreferrer"&gt;this link&lt;/a&gt; and adjust it accordingly. This is also a way to get some credits to the person who posted that answer as I saw this answer while looking out how to set up variable in Azure Pipeline from bash.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tag the image
&lt;/h2&gt;

&lt;p&gt;Because image was built inside Azure pipelines we need to tag it to make push command know where to push the image. Easy way to do it on Linux pipeline is using bash.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bash@3&lt;/span&gt;
      &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;targetType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;inline'&lt;/span&gt;
        &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;docker&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tag&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$(imageRepository):$(tag)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$(containerRegistry)/$(imageRepository):$(tag)'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sometimes you want your image to be tagged with &lt;em&gt;latest&lt;/em&gt; because a lot people will configure (or leave by default) triggers that get activated only on this tag. So let's say you want your &lt;em&gt;development&lt;/em&gt; to be the latest version but &lt;em&gt;main&lt;/em&gt; or &lt;em&gt;master&lt;/em&gt; to be specific version. Below task will activate only if branch name was 'development'.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bash@3&lt;/span&gt;
      &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;targetType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;inline'&lt;/span&gt;
        &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;docker&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tag&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$(imageRepository):$(tag)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$(containerRegistry)/$(imageRepository):latest'&lt;/span&gt;
      &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;eq(variables['Build.SourceBranch'], 'development')&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can read more about it &lt;a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/process/expressions?view=azure-devops" rel="noopener noreferrer"&gt;here&lt;/a&gt; if you need to configure it differently&lt;/p&gt;

&lt;h2&gt;
  
  
  Push the image
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Docker@2&lt;/span&gt;
    &lt;span class="s"&gt;displayName&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and push an image to container registry&lt;/span&gt;
      &lt;span class="s"&gt;inputs&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;push&lt;/span&gt;
        &lt;span class="na"&gt;repository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(imageRepository)&lt;/span&gt;
        &lt;span class="na"&gt;containerRegistry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(dockerRegistryServiceConnection)&lt;/span&gt;
        &lt;span class="na"&gt;buildContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(containerRegistry)&lt;/span&gt;
        &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;$(tag)&lt;/span&gt;
          &lt;span class="s"&gt;latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you didn't tag the image with the &lt;em&gt;latest&lt;/em&gt; tag the whole pipeline will fail as there is no conditional statement. If you don't need it remove it completely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Latest tag (optional)
&lt;/h3&gt;

&lt;p&gt;If you need latest tag conditional this line should help.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;${{ if eq(variables['Build.SourceBranchName'], 'development') }}&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;$(tag)&lt;/span&gt;
    &lt;span class="s"&gt;latest&lt;/span&gt;
&lt;span class="s"&gt;${{ if ne(variables['Build.SourceBranchName'], 'development') }}&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;$(tag)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So task could look like this replacing &lt;em&gt;branchNameGoesHeere&lt;/em&gt; with whatever your branch name should be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Docker@2&lt;/span&gt;
    &lt;span class="s"&gt;displayName&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and push an image to container registry&lt;/span&gt;
      &lt;span class="s"&gt;inputs&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;push&lt;/span&gt;
        &lt;span class="na"&gt;repository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(imageRepository)&lt;/span&gt;
        &lt;span class="na"&gt;containerRegistry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(dockerRegistryServiceConnection)&lt;/span&gt;
        &lt;span class="na"&gt;buildContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(containerRegistry)&lt;/span&gt;
        &lt;span class="s"&gt;${{ if eq(variables['Build.SourceBranchName'], 'development') }}&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;$(tag)&lt;/span&gt;
            &lt;span class="s"&gt;latest&lt;/span&gt;
        &lt;span class="s"&gt;${{ if ne(variables['Build.SourceBranchName'], 'development') }}&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;$(tag)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should be it hope it work :D&lt;/p&gt;

&lt;p&gt;Updates:&lt;br&gt;
I've switched the logic for tagging images with latest as in most cases "latest" version is not the one to deploy nor keep on master/main branches. Obvious reason should be that deploying new code on push should only happen in development/testing environments while there must be a "manual" trigger to fetch the latest &lt;strong&gt;TESTED&lt;/strong&gt; version that &lt;strong&gt;works properly&lt;/strong&gt; and deploy it. Also prior example didn't actually work when having multiple tags and was missing &lt;em&gt;tagging&lt;/em&gt; part so it's added now.&lt;/p&gt;

</description>
      <category>micronaut</category>
      <category>azure</category>
      <category>devops</category>
      <category>docker</category>
    </item>
    <item>
      <title>Collaborative writing?</title>
      <dc:creator>HS</dc:creator>
      <pubDate>Wed, 09 Sep 2020 19:19:38 +0000</pubDate>
      <link>https://forem.com/_hs_/collaborative-writing-164k</link>
      <guid>https://forem.com/_hs_/collaborative-writing-164k</guid>
      <description>&lt;p&gt;It's more of a suggestion for a new feature than discussion but:&lt;/p&gt;

&lt;p&gt;Would it be useful to have collaboration on a post like having 2 people write same article? It's not super complicated feature just like having someone invite others to edit their post before publishing. If post is edited by 1 user other should not be able to edit until first person is done. And both authors are shown in the list. Could be more than 2 people could be expanded to blocking only parts for editing, or collaboration on series... &lt;/p&gt;

&lt;p&gt;Do you know any platform with similar goal as Dev.to that implements this?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>meta</category>
    </item>
    <item>
      <title>Pulsar docker image on Azure</title>
      <dc:creator>HS</dc:creator>
      <pubDate>Thu, 03 Sep 2020 11:45:36 +0000</pubDate>
      <link>https://forem.com/_hs_/pulsar-docker-image-on-azure-mf5</link>
      <guid>https://forem.com/_hs_/pulsar-docker-image-on-azure-mf5</guid>
      <description>&lt;p&gt;I wanted to quick set up Apache Pulsar on Azure. Long time ago this didn't go well but I stumbled on &lt;a href="https://github.com/kafkaesque-io/pulsar-helm-chart" rel="noopener noreferrer"&gt;Kesque helm chart&lt;/a&gt;. Thanks to them it was quite easy. However Microsoft tends to charge quite high for VMs in Azure Kubernetes Service. Now for a startup it gets too high and we don't need that high throughput nor that kind of stability. However if you ever worked with messaging systems like Pulsar, they love to fail if there's too much traffic and VMs are not high-end or they are low in quantity. Also, using storage with AKS would be too much of a price hit so using File Share with Azure Container Instance was best overall solution for us for development environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Apache Pulsar Docker image customisation for JWT
&lt;/h2&gt;

&lt;p&gt;This step is something I wanted but it's not required to make it work on Azure nor anywhere else.&lt;/p&gt;

&lt;p&gt;Apache Pulsar has a nice Docker image. However it does not come with predefined way of using JWT or some other security. In order to do so I've created custom image that changes &lt;code&gt;/pulsar/conf/standalone.conf&lt;/code&gt; to use JWT. This would make it easier for automating development to use simple JWT. First I generated public and private key, then used private key to generate JWTs for superuser, and then &lt;em&gt;stored superuser JWT in container so brokers can communicate&lt;/em&gt;. This also included adding such lines to &lt;strong&gt;standalone.conf&lt;/strong&gt;. If you forget to set these they will make your Pulsar not list clusters let alone tenants, namespaces, and such. Example of lines required to be changed&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# line number about 352
# Enable authentication
&lt;/span&gt;&lt;span class="py"&gt;authenticationEnabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;

&lt;span class="c"&gt;# line number about 358
# Autentication provider name list, which is comma separated list of class names
&lt;/span&gt;&lt;span class="py"&gt;authenticationProviders&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;org.apache.pulsar.broker.authentication.AuthenticationProviderToken&lt;/span&gt;

&lt;span class="c"&gt;#line number about 370
&lt;/span&gt;&lt;span class="py"&gt;superUserRoles&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;superuser&lt;/span&gt;
&lt;span class="c"&gt;#line number about 374-375
&lt;/span&gt;&lt;span class="py"&gt;brokerClientAuthenticationPlugin&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;org.apache.pulsar.client.impl.auth.AuthenticationToken&lt;/span&gt;
&lt;span class="py"&gt;brokerClientAuthenticationParameters&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;file:///pulsar/superuser.jwt&lt;/span&gt;

&lt;span class="c"&gt;# line 384
&lt;/span&gt;&lt;span class="py"&gt;tokenPublicKey&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;file:///pulsar/my-public.key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the difference between &lt;em&gt;org.apache.pulsar.&lt;strong&gt;broker.authentication.AuthenticationProviderToken&lt;/strong&gt;&lt;/em&gt; for &lt;em&gt;authenticationProviders&lt;/em&gt; and &lt;em&gt;org.apache.pulsar.&lt;strong&gt;client.impl.auth.AuthenticationToken&lt;/strong&gt;&lt;/em&gt; for &lt;em&gt;borkerAuth&lt;/em&gt;. Also &lt;em&gt;tokenPublicKey&lt;/em&gt; must be set for Pulsar to be able to verify those JWTs&lt;/p&gt;

&lt;p&gt;First time I put the provider as the client so it didn't work and took about 20 min to figure out :D.&lt;/p&gt;

&lt;p&gt;I also modified client.conf&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;#line 35
&lt;/span&gt;&lt;span class="py"&gt;authPlugin&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;org.apache.pulsar.client.impl.auth.AuthenticationToken&lt;/span&gt;
&lt;span class="c"&gt;#line 42
&lt;/span&gt;&lt;span class="py"&gt;authParams&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;file:///pulsar/superuser.jwt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which is used by tools in bin folder, and websocket.conf same settings as in standalone (maybe different lines).&lt;/p&gt;

&lt;p&gt;Next, the docker image. I used official docker image as base and added lines to copy .jwt and .conf files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; apachepulsar/pulsar:2.6.1&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /pulsar&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; my-private.key my-private.key&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; my-public.key my-public.key&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; superuser.jwt superuser.jwt&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; client.conf conf/client.conf&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; websocket.conf conf/websocket.conf&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; standalone.conf conf/standalone.conf&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; [ "/pulsar/bin/pulsar", "standalone", "-nfw", "-nss" ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Public and private key have been generated before and copied to local machine. You can find in Pulsar official docs how to do it.&lt;/li&gt;
&lt;li&gt;Client, websocket, and standalone conf are modified to use authorisation and need to be copied. I couldn't get something like &lt;strong&gt;RUN sed -i "s|xxx|xxx|g" xxx.conf"&lt;/strong&gt; to work - it would not modify files. I guess it is because it doesn't see the files when building an image and it actually runs command at that point. So I went for fallback method which is copy existing files, modify then push back.&lt;/li&gt;
&lt;li&gt;And finally, strip startup of parameters as much as possible so start Pulsar with the image. Parameters &lt;em&gt;-nfw -nss&lt;/em&gt; are to avoid using functions feature.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Azure Container Instance
&lt;/h2&gt;

&lt;p&gt;Assuming you already know how to do it, first create File Share. We need it so pulsar can store /pulsar/data directory in it in to be able to keep data in cases of restarts, failures, and such. I still haven't figured out how to connect Blob (if possible) so I'm mounting File Shares.&lt;/p&gt;

&lt;p&gt;At the time of writing, Azure didn't support Container Instances to be created with the mount through Web interface so back to terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az container create &lt;span class="nt"&gt;--resource-group&lt;/span&gt; RC_NAME &lt;span class="nt"&gt;--name&lt;/span&gt; CONTAINER_NAME &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--image&lt;/span&gt; YOUR_DOCKER_IMAGE_REPO/IMAGE_TAG &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--registry-username&lt;/span&gt; IMAGE_REPO_USERNAME &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--registry-password&lt;/span&gt; IMAGE_REPO_PASSWORD &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--dns-name-label&lt;/span&gt; SUBDOMAIN_PART_OF_URL_YOU_WANT &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--ports&lt;/span&gt; 8080 6650 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--azure-file-volume-account-name&lt;/span&gt; STORAGE_ACC_NAME &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--azure-file-volume-account-key&lt;/span&gt; STORAGE_ACC_KEY &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--azure-file-volume-share-name&lt;/span&gt; pulsar &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--azure-file-volume-mount-path&lt;/span&gt; /pulsar/data &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--cpu&lt;/span&gt; 4 &lt;span class="nt"&gt;--memory&lt;/span&gt; 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your reading this article, your probably fine with 4 core / 4 gig ram.&lt;/p&gt;

&lt;p&gt;Mounting &lt;em&gt;/pulsar&lt;/em&gt; container path would lead to errors and your container wouldn't start. If you need more than 1 path like &lt;em&gt;/pulsar/conf&lt;/em&gt; and &lt;em&gt;/pulsar/data&lt;/em&gt; you would need to go through -&lt;em&gt;templates&lt;/em&gt;. Reason it &lt;strong&gt;fails on /pulsar mount&lt;/strong&gt; is that, if you haven't guessed it on first look, Pulsar would look into empty file share and wouldn't be able to discover /pulsar/bin/pulsar script and thus report error that file is not found for latest line in Dockerfile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Should work
&lt;/h2&gt;

&lt;p&gt;After couple of seconds / minutes, you should see your container instance up and running.&lt;/p&gt;

&lt;p&gt;If you hit any problems call Azure support team, they "always help" XD.&lt;/p&gt;

</description>
      <category>pulsar</category>
      <category>azure</category>
      <category>aci</category>
      <category>docker</category>
    </item>
    <item>
      <title>MongoDB cross DB update</title>
      <dc:creator>HS</dc:creator>
      <pubDate>Mon, 03 Aug 2020 21:38:48 +0000</pubDate>
      <link>https://forem.com/_hs_/mongodb-cross-db-update-3fib</link>
      <guid>https://forem.com/_hs_/mongodb-cross-db-update-3fib</guid>
      <description>&lt;h1&gt;
  
  
  Why?
&lt;/h1&gt;

&lt;p&gt;I found myself in funny situation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Spring Data for MongoDB adds &lt;strong&gt;_class&lt;/strong&gt; field to anything&lt;/li&gt;
&lt;li&gt;I use interface as a property in some cases&lt;/li&gt;
&lt;li&gt;Interface relies on &lt;strong&gt;@JsonSubTypes&lt;/strong&gt; from &lt;strong&gt;Jackson&lt;/strong&gt; so I can easily have anything in DB as that property and let Jackson and Spring do the rest&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;I refactored code changing package names&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Why write about it here? I need somewhere to put this piece of code so I don't have to Google if I do the same mistake again.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  The problem
&lt;/h1&gt;

&lt;p&gt;First of all, problem wasn't in the fact that aggregating class had different package name. No, in fact it works with main class easily as it's linked to a &lt;em&gt;collection&lt;/em&gt; and there's only 1 specific class used for that particular collection. Issue is with this dynamic property supporting many different implementations while Spring is unable to detect what to instantiate since the &lt;strong&gt;_class&lt;/strong&gt; field contains incorrect value. I got something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;springframework&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mapping&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MappingInstantiationException&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Failed&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;instantiate&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;your&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;old&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;package&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="n"&gt;using&lt;/span&gt; &lt;span class="n"&gt;constructor&lt;/span&gt; &lt;span class="no"&gt;NO_CONSTRUCTOR&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I did spend a lot of time trying to guess what was updated and what to change in &lt;strong&gt;code&lt;/strong&gt; and somehow, it clicked. I forgot how I remembered it but I know that I just went straight to DB and started changing some values. It's easy for 1 collection in 1 database you put in&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;someCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateMany&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and viola. But I have multitenant system. And yes I do use DB(s) per customer. Good thing only Mongo was affected by this as I do have more than 1 DB system for each customer.&lt;/p&gt;

&lt;p&gt;So I wanted to update all of this by just one single command (not line necessarily)&lt;/p&gt;

&lt;h1&gt;
  
  
  The solution
&lt;/h1&gt;

&lt;p&gt;Minimal version 4.2 for updateMany() with $set option&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;adminCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;listDatabases&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;databases&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dbn&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;dbc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSiblingDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dbn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;dbc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;geoJsonFeature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dynamicProperty._class&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;$regex&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/.*redp&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;GeoPolygon.*/&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;$set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dynamicProperty._class&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;some.package.name.ImplementingClass&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break it down a bit&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;db.adminCommand( { listDatabases: 1 } )&lt;/em&gt; will return object containing list of databases so adding &lt;em&gt;.databases&lt;/em&gt; will list databases as simple objects.&lt;/li&gt;
&lt;li&gt;map the name only&lt;/li&gt;
&lt;li&gt;use the for each to loop through all names&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;db.getSiblingDB(dbn)&lt;/em&gt; will switch which DB is used&lt;/li&gt;
&lt;li&gt;updateMany with $set because replaceAll and replaceOne are from 4.4 version&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's a bit nasty "solution" but if you need to update multi tenant system where you need something trivial and all tenants have same schema for a certain collection this is quick and dirty solution for it.&lt;/p&gt;

&lt;p&gt;You can also combine it with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCollectionNames&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCollection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)...&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to go wild :D.&lt;/p&gt;

&lt;p&gt;That's all&lt;/p&gt;

</description>
      <category>mongodb</category>
      <category>springdata</category>
      <category>multitenant</category>
    </item>
    <item>
      <title>SDN/RX Cypher tips</title>
      <dc:creator>HS</dc:creator>
      <pubDate>Tue, 19 May 2020 13:45:57 +0000</pubDate>
      <link>https://forem.com/_hs_/sdn-rx-cypher-tips-1n98</link>
      <guid>https://forem.com/_hs_/sdn-rx-cypher-tips-1n98</guid>
      <description>&lt;p&gt;I spent some time working on a project using Neo4j and Spring with Kotlin. I decided to go with WebFlux as it's reactive/all that non-blocking stuff. Now as SDN/RX is fairly new (out of beta since April 2020 I think) there has been some confusion, given that I used OGM for previous stuff I unintentionally tried to combine two approaches and it didn't work as expected. Trying to adjust to new framework took a bit and I realised some features were not only changed but missing completely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Embedded objects (like in OGM) problem
&lt;/h2&gt;

&lt;p&gt;Embedded objects at the time of writting are not yet supported as in OGM. This means that nesting object in another one inside of the code will produce relationship rather than the embedded object. For status update follow &lt;a href="https://github.com/neo4j/sdn-rx/issues/213" rel="noopener noreferrer"&gt;this GitHub issue&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  One to one implicit relationship
&lt;/h2&gt;

&lt;p&gt;As stated in previous point, if you have complex/custom types for properties in your &lt;code&gt;class&lt;/code&gt;, those will be resolved as Node to Node relatioship.&lt;br&gt;
Having embedded objects represented as relationships can make custom queries a bit hard if your going with annotation approach. So let's say you have&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;LanguageStuff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt; &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;en&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;ba&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Stuff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="nd"&gt;@Id&lt;/span&gt; &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;serial&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="c1"&gt;//This one will be automatically generated as related node&lt;/span&gt;
   &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LanguageStuff&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="c1"&gt;//and this one&lt;/span&gt;
   &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LanguageStuff&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even though there's no &lt;code&gt;@Relationship&lt;/code&gt; attribute the &lt;em&gt;description&lt;/em&gt; property will be generated as separate Node in Neo4j and therefor, will be fetched automatically each time we use common methods of querying data like letting spring resolve method name and try to generate query or generating Cypher with tools provided by SDN/RX as Cypher DSL which you can read more about &lt;a href="https://neo4j.github.io/sdn-rx/current/#cypher-dsl" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom queries using annotations
&lt;/h2&gt;

&lt;p&gt;This all works well when you only need default behaviour. If you need annotated method with query including custom Cypher it gets a bit tricky to spot some things.&lt;/p&gt;

&lt;p&gt;I did put &lt;code&gt;name&lt;/code&gt; property intentionally using the same type as description. This is due to missing detailed note about caching and how objects are resolved by SDN/RX. Given repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Stuffpository&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ReactiveNeo4jRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Stuff&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MATCH (s:Stuff) RETURN s"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;customCypherStuff&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might forget about the fact that Kotlin needs &lt;code&gt;?&lt;/code&gt; if things can be nullable. And there you go with the exception of &lt;code&gt;could not be mapped&lt;/code&gt;. If you need those values you would want to fetch &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt; represented with &lt;code&gt;LanguageStuff&lt;/code&gt; data class. If your lazy enough like me you would try something like &lt;code&gt;@Query("MATCH p=(s:Stuff)-[*]-&amp;gt;(:LanguageStuff) RETURN p")&lt;/code&gt; and of course this doesn't work because it would be too much magic to resolve paths, but it's fun to break stuff and boring to read the docs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cypher "spread" operator and join properties
&lt;/h2&gt;

&lt;p&gt;But if you think a bit harder you might end up with something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;s:&lt;/span&gt;&lt;span class="n"&gt;Stuff&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;desc:&lt;/span&gt;&lt;span class="n"&gt;LanguageStuff&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;name:&lt;/span&gt;&lt;span class="n"&gt;LanguageStuff&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt;&lt;span class="py"&gt;description:&lt;/span&gt;&lt;span class="k"&gt;desc&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt;&lt;span class="py"&gt;name:&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="ss"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;inside your &lt;code&gt;@Query&lt;/code&gt; value. If you're reading this by accident or you are new to this and don't know much about Cypher, might be a good note to point out that &lt;code&gt;s{.*}&lt;/code&gt; is something like spread operator &lt;code&gt;.*&lt;/code&gt; where each property/attribute from given &lt;code&gt;s&lt;/code&gt; will be put to new object &lt;code&gt;{}&lt;/code&gt;. As &lt;code&gt;s&lt;/code&gt; only contains &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;serial&lt;/code&gt; as real properties and &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt; are related nodes, only first two will be &lt;em&gt;spread&lt;/em&gt; from main node. Second two are explicitly set. &lt;strong&gt;Again this doesn't get the result&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  SDN/RX "hidden" naming
&lt;/h2&gt;

&lt;p&gt;Graphs are not tables. There's no "real" one to one relationship so mapping nodes to data class fails. Then we can use arrays which solves problem up to some point&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;s:&lt;/span&gt;&lt;span class="n"&gt;Stuff&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;desc:&lt;/span&gt;&lt;span class="n"&gt;LanguageStuff&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;name:&lt;/span&gt;&lt;span class="n"&gt;LanguageStuff&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt;&lt;span class="py"&gt;Stuff_DESCRIPTION_LanguageStuff:&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="k"&gt;desc&lt;/span&gt;&lt;span class="ss"&gt;],&lt;/span&gt;&lt;span class="py"&gt;Stuff_NAME_LanguageStuff:&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="ss"&gt;]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why the ugly names? SDN/RX by default maps relationships in naming format of &lt;code&gt;ClassName_CAPITAL LETTERS OF PROPPERTY NAME IF RELATIONSHIP NAME IS NOT SET_PropertyClassName&lt;/code&gt;. So SDN/RX actually maps the relationships and generates special names for them for embedded objects (&lt;strong&gt;for now!&lt;/strong&gt;). And therefor, queries need to fetch array named with relationship type where SDN/RX kicks in and resolves where to map what &lt;strong&gt;BUT we face yet another problem&lt;/strong&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  SDN/RX auto generated ID problem
&lt;/h2&gt;

&lt;p&gt;You get things mapped but you will notice that &lt;strong&gt;both &lt;code&gt;name&lt;/code&gt; and the &lt;code&gt;description&lt;/code&gt; have the same value&lt;/strong&gt;. &lt;br&gt;
My best guess is that SDN/RX does some caching by type and both properties are the same type, but also, both properties have ID generated in default manner as a Long. This means that ID is not stored as separate/special property in the Neo4j Node but you need to actually resolve it by using &lt;code&gt;id(node)&lt;/code&gt;. This may be done in the background for automated way of generating queries but when you write a custom one you need to remember to know how does the mapper work since you haven't put in any details like relationship name or such.&lt;br&gt;
I'm unaware am I right about "caching" where it could be that it's using type aware Map but it does ignore rest of the objects of the same type. The main problem is that Id is not being fetched so we end up with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;s:&lt;/span&gt;&lt;span class="n"&gt;Stuff&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;desc:&lt;/span&gt;&lt;span class="n"&gt;LanguageStuff&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;name:&lt;/span&gt;&lt;span class="n"&gt;LanguageStuff&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt;&lt;span class="py"&gt;Stuff_DESCRIPTION_LanguageStuff:&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="k"&gt;desc&lt;/span&gt;&lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="n"&gt;.en&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt;&lt;span class="n"&gt;.ba&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt;&lt;span class="py"&gt;__internalNeo4jId__:&lt;/span&gt; &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="k"&gt;desc&lt;/span&gt;&lt;span class="ss"&gt;)}],&lt;/span&gt;&lt;span class="py"&gt;Stuff_NAME_LanguageStuff:&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="n"&gt;.en&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt;&lt;span class="n"&gt;.ba&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt;&lt;span class="py"&gt;__internalNeo4jId__:&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="ss"&gt;)}]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;__internalNeo4jId__&lt;/code&gt; is name to use for IDs generated and used by Neo4j to tell SDN/RX to actually map it. This way SDN/RX finally maps correct values to correct places.&lt;/p&gt;

&lt;p&gt;As said, I'm unaware how does wrongful mapping of different objects of the same type happens but it does for SDN/RX 1.0.1 version when using generated IDs by default method. I guess using custom generator will prevent this as ID would be stored as separate attributed and then mapped back again automatically when doing object graph mapping.&lt;/p&gt;

&lt;p&gt;That's it for now&lt;/p&gt;

</description>
      <category>neo4j</category>
      <category>sdnrx</category>
      <category>spring</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>Help: Security per record - is it possible to make optimal solution ?</title>
      <dc:creator>HS</dc:creator>
      <pubDate>Wed, 06 May 2020 21:31:18 +0000</pubDate>
      <link>https://forem.com/_hs_/help-security-per-record-is-it-possible-to-make-optimal-solution-4a62</link>
      <guid>https://forem.com/_hs_/help-security-per-record-is-it-possible-to-make-optimal-solution-4a62</guid>
      <description>&lt;p&gt;So I have a case where data structure looks more like a graph so lets say (object)-&amp;gt;(another one)-&amp;gt;(yes, antoher)-&amp;gt;(even more). And this is stored in graph DB but there's "piled" data which is stored in different engine as pile of things referenced by some filed to vertex/node in graph one.&lt;/p&gt;

&lt;p&gt;My problem is that I need security "user per object" where each user can have different access rights, and no role will help in this case as most of the users have different access rights even when they work at same position. So this is what I would call access grant per record or per object.&lt;/p&gt;

&lt;p&gt;Now if someone has access to particular object he automatically has access to parent objects. But for example lets say I have (A)-&amp;gt;(B) and (A)-&amp;gt;(C). If I can access B I should be able to get A but not necessarily C.&lt;/p&gt;

&lt;p&gt;I do plan to integrate something like HashiCorp Consul or if there's another similar thing which is easy to use but I don't think this can help at all. I also do have SSO but as any other it mainly relies on roles and such.&lt;/p&gt;

&lt;p&gt;BTW, system is multi tenant so adding a new record to a specific user should be visible only for that particular client users, and Admin should decide who get's to see what.&lt;/p&gt;

&lt;p&gt;So couple of ways to do it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Have enormous amount of custom queries which filter out data - slow, hard to maintain and possible to leak data when new features are added or changes are introduced.&lt;/li&gt;
&lt;li&gt;Middleware that handles access per object only - also slow due to a lot of network calls like Client -&amp;gt; load balancer -&amp;gt; API -&amp;gt; DB -&amp;gt; API -&amp;gt; middleware -&amp;gt; DB of the middleware -&amp;gt; middleware -&amp;gt; API -&amp;gt; load balancer -&amp;gt; Client. This could be used with some form of in memory cache which would speed up thing to some extent.&lt;/li&gt;
&lt;li&gt;Have in-memory filter where each record stored and modified by admin has it's access towards each user cached - extra expensive to have huge amounts of RAM for such a cache &lt;/li&gt;
&lt;li&gt;Have SSO deal with it through roles - now I have no idea how would I automate adding roles for each new object inserted to the system, and why would I have so much roles. Users have single access for SSO, so the actual roles would have to spill all over place as there's things like user from one tenant can be added to access some of the data of another tenant...&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Anyone have ideas on how to deal with this in optimal way? I really do need help, maybe some software for this I didn't know exits or using SSO or Consul properly somehow for this?&lt;/p&gt;

</description>
      <category>help</category>
      <category>security</category>
      <category>oidc</category>
    </item>
    <item>
      <title>Outsourcing state of mind in software development</title>
      <dc:creator>HS</dc:creator>
      <pubDate>Wed, 15 Apr 2020 13:24:55 +0000</pubDate>
      <link>https://forem.com/_hs_/outsourcing-state-of-mind-in-software-development-1ob3</link>
      <guid>https://forem.com/_hs_/outsourcing-state-of-mind-in-software-development-1ob3</guid>
      <description>&lt;p&gt;Last ~8 months I started working in a non software development company. What it means is that I used to work for companies which only had interest in developing software for others (outsourcing) and therefor, my job was to read tickets/issues/whatever on JIRA or such software and put some code into app/service/web by copying previous code of the "wise men before me" and changing it just enough to fit the need of the current task. Gradually you start learning patterns as a demand for job position and start "copying" stuff from your head instead from previous code writers or at that point stackoverflow. Now you might think well that's the way to do it but by changing jobs and getting more and more interaction with other people on the project (besides developers or IT guys) and in the end, with clients requesting the project, I started to get in conflict with my bosses, system architect, and such people by just asking questions like "is this task 100% correct", "should this be in the system at all", "I didn't understand client the same way you did", "why must we use X technology and do it in months when we can switch to Z tech for this part of the task and do it in 3 days while having it super easy to change later without bringing down the system".&lt;/p&gt;

&lt;p&gt;I learned a lot about copy pasting without thinking like following patterns, DDD as explained by others, using libraries and frameworks for every piece of tiny problem, and how I'm not good enough to ever develop such a solution or my code always needs to be reviewed by boss (usually developers that founded the company in my case) or some external boss like system architect from company outsourcing job in absence of workforce on theirside.&lt;/p&gt;

&lt;p&gt;What I didn't learn is how to use my own head and actually talk to people once I spot the problem... Thing was they didn't learn how to do projects just how to brag about being "full stack tech dev ops sec architecutre TDD DDD certified..." you get the point right? Now last switch was from software developer used to getting orders to being called backend developer while doing system architecture, cost planning, DevOps, project management for backend, partial involvement into new feature planing (yes, not only client requests features but I had to think of some too), team management and such. And WOW. What a responsibility change. I found out guys with 15 years of experience (whereas I have a bit less than 7 as time of writting) have less idea about development than I do simply because they never got out of "outsourcing state of mind". They got the "guy" working here before, he will make tasks in JIRA, tell them what to do and they will just put in some code. Boy what kind of well written useless code have I seen, from completely missing the point of task to skipping crucial steps because "I'll get back to it I didn't have info" - where later one is mindset in terms of "this is something we can consider template, we just swap later" and this one was wrong on so many levels.&lt;/p&gt;

&lt;p&gt;I'm not encouraging devs to fight their bosses, go against their will, question everything and refuse to do obviously stupid stuff. I'm just saying thinking can lead to a great product, if your supervisors disagree, you can shut up do your work and change your employer ASAP orrrrr &lt;strong&gt;start up your own company&lt;/strong&gt;. No I didn't have b** to risk my non existing finances and bankrupt my wife and me but you might and you should. Just because I chickened out many times doesn't mean you should. My wife had ideas, I wanted to start a lot of stuff but never was I willing to leave paid job and live under tent to try to make my own goals. I might try at some point but this post is not about that.&lt;/p&gt;

&lt;p&gt;Have you ever had such opinion on industry that it's discouraging usefull product development in favour of well written software that misses the point of feature requests or maybe even well written useless software?&lt;/p&gt;

&lt;p&gt;I might be under heavy stress now, work too much, learning 6 things at one just to make ends meet in product, my employers might have no respect towards effort required to build such a huge system, but that doesn't mean I will quit nor does it mean its worse than before. In fact, this is the first company where I can see how useful can thing I'm building be. I don't think I'm skilled enough or smart enough for this project to make it's architecture and make it work well but no one accepted the job before me. I wasn't told what I was getting into, and in the end when we tried to find skilled architect and other developers, one refused and that's about it no application no nothing. So someone has to build this, someone has to try and make it work, so since I'm here it might as well be me.&lt;/p&gt;

&lt;p&gt;However do you think you would accept average salary for this kind of job? Some people might refuse this position under statement "not enough paid" or "because they like their job". But some of them will brag about their skill proficiency, how much books they read, and how what kind of products they read, but both you and I know that some of those would not stand a chance without being told WHAT to do but just given feature requests and left to handle the whole project. Why am I saying this negative stuff? Well it turns out that a lot of devs I had contact with are just fine with being ordered around and have no intention of ever knowing what to do. Why is that so bad? Well it's great to be that developer but someone has to be in charge of the project, someone has to make tasks, think ahead and so on. I wish I wasn't that guy because I do not think that I'am ready (skill-full) to do so but since I'm already in it I gotta do my best. Important thing for me was to inform people "hey guys this is too much for me" and they were like "yeah we know, we're gonna try to find someone but keep on the work".&lt;/p&gt;

&lt;p&gt;Any similar experience? Any such negative opinions about certain people that you crossed career-path with?&lt;/p&gt;

&lt;p&gt;Hope I didn't come out as someone who just nags about stuff.&lt;/p&gt;

</description>
      <category>career</category>
    </item>
    <item>
      <title>A dev on UX</title>
      <dc:creator>HS</dc:creator>
      <pubDate>Wed, 08 Apr 2020 18:04:47 +0000</pubDate>
      <link>https://forem.com/_hs_/a-dev-on-ux-without-any-knowledge-in-field-4nmm</link>
      <guid>https://forem.com/_hs_/a-dev-on-ux-without-any-knowledge-in-field-4nmm</guid>
      <description>&lt;p&gt;So I've been working with IntelliJ (sadly on Windows) and I've got so angry when once more shifting focus made me mess up code and build to fail in 3 seconds window.&lt;/p&gt;

&lt;p&gt;What happens is that I click Run for Spring Boot application while having my Terminal in Intellij opened. For some reason it takes about 3 seconds to start the process and meanwhile I'm writing some commands like "git add ." where my lovely tool switches focus to some file, so I'm typing "git ad" to console and "d ." into code and Intellij switches to tab Run/Debug then to Build and shows crash log because my file contains "d ." in middle of the code.&lt;/p&gt;

&lt;p&gt;Now I instantly reported this as a bug on their tracker because it reeeeeeeeaaaaally pi*e* me of. And they are quite good compared to others. Imagine starting installer, putting it into background, writing code and stuff, refactoring things in code with famous Ctrl + F6 and all of a sudden instead of hitting Enter/Return on "Rename" you hit it on installer window asking you to accept all for installing McAfee, toolbar for your non existing IE, some stuff from 95, spyware for government, cookies for your grandmother, child support for other peoples kids and so on.&lt;/p&gt;

&lt;p&gt;Really? A lot of installers wont do this but why is it even possible to make such app that steals focus? Have people not been multitasking lately with 11293810 core leaving stuff to run in the background instead of "You must wait until other installers finish!" message from half-core CPU times?? &lt;/p&gt;

&lt;p&gt;Who thought every software is so special and important that they need to blast you as a pop-up before Opera or before ad-blockers on other browsers? Why oh why would anyone do this? Blinking light in the taskbar or notification popups in specific corner are not good enough for snoflware software so OS just allows it to torture me?&lt;/p&gt;

&lt;p&gt;Dear UI/UX people please talk with other people that will actually use software, see their experience, stop assuming your building most important thing and just make everything so important that it steals your focus.&lt;/p&gt;

&lt;p&gt;Now a lot of things are amazing, great, and surprise me how well are they done and thought through when it comes to UX but some of the stuff make me write these kinds of posts.&lt;/p&gt;

</description>
      <category>ux</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Apache Pulsar on AKS quick setup for development</title>
      <dc:creator>HS</dc:creator>
      <pubDate>Sat, 28 Mar 2020 22:10:27 +0000</pubDate>
      <link>https://forem.com/_hs_/apache-pulsar-on-aks-quick-setup-for-development-4m91</link>
      <guid>https://forem.com/_hs_/apache-pulsar-on-aks-quick-setup-for-development-4m91</guid>
      <description>&lt;p&gt;THANKS to guys at kafkaesque-io there's &lt;a href="https://github.com/kafkaesque-io/pulsar-helm-chart" rel="noopener noreferrer"&gt;https://github.com/kafkaesque-io/pulsar-helm-chart&lt;/a&gt; that will make it much easier to setup.&lt;/p&gt;

&lt;h1&gt;
  
  
  If you love Azure or Microsoft skip this part
&lt;/h1&gt;

&lt;p&gt;I dislike Azure due to a lot of time wasted on trying to configure it where at the end I would eventually give up and use whatever was possible or just give up and tell my employer that this is not possible on Azure.&lt;/p&gt;

&lt;p&gt;Some examples include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Impossible to setup MongoDB as docker image via &lt;em&gt;Container Instance&lt;/em&gt; which would enable easy setup for dev server. Short: it's not possible to attach Azure file share so mongo can use it. It will create a couple of folders and give up as "permission denied". However neo4j passed it.&lt;/li&gt;
&lt;li&gt;Network. I had great pain setting up Spring Boot and Micronaut Neo4j clients just to realise that App Services cannot go over 230 seconds of idle TCP but Azure default load balancer or server or whatever under these connections will not properly close connections. Instead, it will just drop it leaving things like SDN/RX, which rely on TCP communication proper closing, think that connection is still there. Therefor, you'll see 20 sec connection pending until your app gets a hang of it realises no Neo4j connection in pool actually works then drop it and create new. Acquire time has nothing to do with this as connections will be visible to clients and only way to detect it is to have transaction timeout where client knows that if X amount of time is not enough for transaction there's a connection problem. Easy fix was to use connection lifetime on client settings where connections have to end in 230 seconds or 3.8 minutes. Maybe 228s as I set it just to be sure to break existing connection in pool before it ends and gets pending state.&lt;/li&gt;
&lt;li&gt;Anything you touch or do with it burns. Each software will come with instructions for AWS and a lot with GCP. A LOOOOOOOOOOOOOOT come without any info on Azure or special info saying trillions of steps on how to enable it somehow and some of the features are not available.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I really did consider quitting my job and changing career just to be safe from the hell Microsfot puts his Azure clients through. And not to mention Blob Storage hell and prices.&lt;/p&gt;

&lt;h1&gt;
  
  
  Preps
&lt;/h1&gt;

&lt;p&gt;However, job is a job, depression goes away and you still need to do stuff. Latest was the Apache Pulsar. I needed to get this up and running just to keep on working and I'm blocking the whole team.&lt;/p&gt;

&lt;p&gt;Good thing kafkaesque.io guys put together helm chart that has Azure included.&lt;/p&gt;

&lt;p&gt;First of all I want to mention that you need at least 6 nodes in AKS pool with machines that have 2 CPU and 4GB RAM. Because of companies subscription and previous AKS setup I had to remove AKS and add a new one with 5 nodes of 2 cpus + 4GB ram. Good enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker to speed up generating tokens prior to deploy
&lt;/h2&gt;

&lt;p&gt;I actually used docker to run my own standalone pulsar to generate keys upfront. If you do it like that generate all keys on docker. You can jump in to pulsar docker instance like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;pulsar&lt;/span&gt;&lt;span class="sh"&gt; container id&amp;gt;&amp;gt; /bin/bash
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Than generate stuff like explained in kafkaesque.io git repo for helm chart for pulsar under Authorisation section. Too lazy to explore well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bin/pulsar tokens create-key-pair &lt;span class="nt"&gt;--output-private-key&lt;/span&gt; my-private.key &lt;span class="nt"&gt;--output-public-key&lt;/span&gt; my-public.key
bin/pulsar tokens create &lt;span class="nt"&gt;--private-key&lt;/span&gt; file:///pulsar/my-private.key &lt;span class="nt"&gt;--subject&lt;/span&gt; admin &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; admin.jwt
bin/pulsar tokens create &lt;span class="nt"&gt;--private-key&lt;/span&gt; file:///pulsar/my-private.key &lt;span class="nt"&gt;--subject&lt;/span&gt; superuser &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; superuser.jwt
bin/pulsar tokens create &lt;span class="nt"&gt;--private-key&lt;/span&gt; file:///pulsar/my-private.key &lt;span class="nt"&gt;--subject&lt;/span&gt; websocket &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; websocket.jwt
bin/pulsar tokens create &lt;span class="nt"&gt;--private-key&lt;/span&gt; file:///pulsar/my-private.key &lt;span class="nt"&gt;--subject&lt;/span&gt; proxy &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; proxy.jwt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default everything is setup by those guys to work with those names. So it's really important that filename for keys are &lt;em&gt;my-private.key&lt;/em&gt; and &lt;em&gt;my-public.key&lt;/em&gt;. This is assuming that you want to do as less as possible with configuring anything.&lt;/p&gt;

&lt;p&gt;Next you want to have all of those files on your PC for easier use&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;your&lt;/span&gt;&lt;span class="sh"&gt; pulsar docker id&amp;gt;&amp;gt;:/pulsar/my-private.key my-private.key
docker cp &amp;lt;&amp;lt;your pulsar docker id&amp;gt;&amp;gt;:/pulsar/my-private.key my-private.key
docker cp &amp;lt;&amp;lt;your pulsar docker id&amp;gt;&amp;gt;:/pulsar/my-public.key my-public.key
docker cp &amp;lt;&amp;lt;your pulsar docker id&amp;gt;&amp;gt;:/pulsar/admin.jwt admin.jwt
docker cp &amp;lt;&amp;lt;your pulsar docker id&amp;gt;&amp;gt;:/pulsar/proxy.jwt proxy.jwt
docker cp &amp;lt;&amp;lt;your pulsar docker id&amp;gt;&amp;gt;:/pulsar/websocket.jwt websocket.jwt
docker cp &amp;lt;&amp;lt;your pulsar docker id&amp;gt;&amp;gt;:/pulsar/superuser.jwt superuser.jwt
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reason for "one by one file" is that you see all of the files used in here. You can have some shorthand command if you like.&lt;/p&gt;

&lt;p&gt;Also I had plugin for Kubernets in Visual Studio Code which made it easy to check Azure AKS and right click on them then and choose &lt;em&gt;Merge into Kubeconfig&lt;/em&gt;. Using docker this was super easy and then you can switch via docker to AKS. Just to see what I'm talking about:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4z56y7d7qwp8mbkkupvg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4z56y7d7qwp8mbkkupvg.png" alt="Alt Text" width="394" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why? Well I'm too lazy to configure stuff so this was easy. Also on Windows docker will have tray icon with right click option Kubernetes where you can see your AKS in list after previous step and click on it to switch it very easy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Push secrets to AKS
&lt;/h2&gt;

&lt;p&gt;If you skipped Docker part please note here that it's important to &lt;strong&gt;have same names for files in secrets as secret names also&lt;/strong&gt;. This is because it's already set up to work with these names and probably for development you won't care enough to change it so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create secret generic token-public-key &lt;span class="nt"&gt;--from-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-public.key &lt;span class="nt"&gt;--namespace&lt;/span&gt; pulsar
kubectl create secret generic token-private-key &lt;span class="nt"&gt;--from-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-private.key &lt;span class="nt"&gt;--namespace&lt;/span&gt; pulsar
kubectl create secret generic token-admin &lt;span class="nt"&gt;--from-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin.jwt &lt;span class="nt"&gt;--namespace&lt;/span&gt; pulsar
kubectl create secret generic token-superuser &lt;span class="nt"&gt;--from-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;superuser.jwt &lt;span class="nt"&gt;--namespace&lt;/span&gt; pulsar
kubectl create secret generic token-websocket &lt;span class="nt"&gt;--from-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;websocket.jwt &lt;span class="nt"&gt;--namespace&lt;/span&gt; pulsar
kubectl create secret generic token-proxy &lt;span class="nt"&gt;--from-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;proxy.jwt &lt;span class="nt"&gt;--namespace&lt;/span&gt; pulsar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Installation - for development
&lt;/h1&gt;

&lt;p&gt;For development means not storage. Why? Well it's too expensive. &lt;/p&gt;

&lt;p&gt;If you want you can add your Container Registry from Azure first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm registry login &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;yourazure&lt;/span&gt;&lt;span class="sh"&gt;&amp;gt;&amp;gt;.azurecr.io --username &amp;lt;&amp;lt;listed on azure&amp;gt;&amp;gt;--password &amp;lt;&amp;lt;so is your_pass&amp;gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, add helm repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm repo add kafkaesque https://helm.kafkaesque.io
helm repo update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
 Why update? Well I have some form of OCD when it comes to updates so I like to update stuff as soon as I add new repo. Hope that's fine.&lt;/p&gt;

&lt;p&gt;If you want to enable Authorisation you need to add extra line at the top&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;enableTokenAuth: yes&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 to example development yaml so:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I also removed pulsar functions because I don't need it&lt;/strong&gt;*&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;persistence&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no&lt;/span&gt;
&lt;span class="na"&gt;enableAntiAffinity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no&lt;/span&gt;
&lt;span class="c1"&gt;#this is missing in their repo file&lt;/span&gt;
&lt;span class="na"&gt;enableTokenAuth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;

&lt;span class="na"&gt;zookeeper&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;512Mi&lt;/span&gt;
      &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.3&lt;/span&gt; 
  &lt;span class="na"&gt;configData&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;PULSAR_MEM&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;-Xms512m&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-Xmx512m&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-Dcom.sun.management.jmxremote&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-Djute.maxbuffer=10485760&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:+ParallelRefProcEnabled&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:+UnlockExperimentalVMOptions&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:+AggressiveOpts&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:+DoEscapeAnalysis&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:+DisableExplicitGC&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:+PerfDisableSharedMem&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-Dzookeeper.forceSync=no&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;

&lt;span class="na"&gt;bookkeeper&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicaCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;512Mi&lt;/span&gt;
      &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.3&lt;/span&gt; 
  &lt;span class="na"&gt;configData&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;PULSAR_MEM&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;-Xms512m&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-Xmx512m&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:MaxDirectMemorySize=512m&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-Dio.netty.leakDetectionLevel=disabled&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-Dio.netty.recycler.linkCapacity=1024&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:+UseG1GC&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:MaxGCPauseMillis=10&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:+ParallelRefProcEnabled&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:+UnlockExperimentalVMOptions&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:+AggressiveOpts&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:+DoEscapeAnalysis&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:ParallelGCThreads=32&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:ConcGCThreads=32&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:G1NewSizePercent=50&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:+DisableExplicitGC&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:-ResizePLAB&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:+ExitOnOutOfMemoryError&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:+PerfDisableSharedMem&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:+PrintGCDetails&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:+PrintGCTimeStamps&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:+PrintGCApplicationStoppedTime&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:+PrintHeapAtGC&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-verbosegc&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:G1LogLevel=finest&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;

&lt;span class="na"&gt;broker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;broker&lt;/span&gt;
  &lt;span class="na"&gt;replicaCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;512Mi&lt;/span&gt;
      &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.3&lt;/span&gt; 
  &lt;span class="na"&gt;configData&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;PULSAR_MEM&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;-Xms512m&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-Xmx512m&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:MaxDirectMemorySize=512m&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-Dio.netty.leakDetectionLevel=disabled&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-Dio.netty.recycler.linkCapacity=1024&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:+ParallelRefProcEnabled&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:+UnlockExperimentalVMOptions&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:+AggressiveOpts&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:+DoEscapeAnalysis&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:ParallelGCThreads=32&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:ConcGCThreads=32&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:G1NewSizePercent=50&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:+DisableExplicitGC&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:-ResizePLAB&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:+ExitOnOutOfMemoryError&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:+PerfDisableSharedMem&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;

&lt;span class="na"&gt;autoRecovery&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1Gi&lt;/span&gt;
      &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;

&lt;span class="na"&gt;proxy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicaCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1&lt;/span&gt; 
  &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;512Mi&lt;/span&gt;
      &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.3&lt;/span&gt; 
  &lt;span class="na"&gt;wsResources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;512Mi&lt;/span&gt;
      &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.3&lt;/span&gt;
  &lt;span class="na"&gt;configData&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;PULSAR_MEM&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;-Xms512m&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-Xmx512m&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:MaxDirectMemorySize=512m&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now apply this as installation. A little heads up (for users on Windows and helm 3+ I think but may be all of you): Docs on previous github repo link do not use name for installing repo so add it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm &lt;span class="nb"&gt;install &lt;/span&gt;pulsar &lt;span class="nt"&gt;--namespace&lt;/span&gt; pulsar kafkaesque/pulsar &lt;span class="nt"&gt;--values&lt;/span&gt; &lt;span class="s1"&gt;'C:\Users\&amp;lt;&amp;lt;YourUsername&amp;gt;&amp;gt;\Desktop\test_helm\dev_values.yml'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
 This means that first &lt;strong&gt;pulsar&lt;/strong&gt; in the command is actually name of the installation inside of your AKS or whichever proper word is used instead of installation. Anyways it's missing at the time of writing in the repo example.&lt;/p&gt;

&lt;p&gt;Wait a bit and do a quick check with "kubectl get pods". If everything is initialised you can use&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl expose service pulsar-proxy &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;LoadBalancer &lt;span class="nt"&gt;--name&lt;/span&gt; pulsar-exposed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will get you and External IP for using pulsar outside of AKS. Use that IP with 6650 or 8080 and admin.jwt (I think this one is for basic use).&lt;/p&gt;

&lt;p&gt;Good luck. &lt;/p&gt;

</description>
      <category>apachepuslar</category>
      <category>azure</category>
      <category>kubernetes</category>
      <category>kafkaesqueio</category>
    </item>
  </channel>
</rss>
