<?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>DEV Community: force_push</title>
    <description>The latest articles on DEV Community by force_push (@force_push).</description>
    <link>https://dev.to/force_push</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%2F975008%2Fd750c682-083d-4063-a0cb-ce0c03b5acd4.png</url>
      <title>DEV Community: force_push</title>
      <link>https://dev.to/force_push</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/force_push"/>
    <language>en</language>
    <item>
      <title>Mutation Testing: delicious concept you will rarely use in practice</title>
      <dc:creator>force_push</dc:creator>
      <pubDate>Mon, 10 Apr 2023 08:06:21 +0000</pubDate>
      <link>https://dev.to/force_push/mutation-testing-delicious-concept-you-will-rarely-use-in-practice-3dm6</link>
      <guid>https://dev.to/force_push/mutation-testing-delicious-concept-you-will-rarely-use-in-practice-3dm6</guid>
      <description>&lt;h2&gt;
  
  
  Mutation Testing
&lt;/h2&gt;

&lt;p&gt;Code coverage is a fairly standard metric, and many of us use it to assess unit testing. After all, if certain lines of code are never called by tests, it is likely the scenarios that use them are not fully tested. &lt;/p&gt;

&lt;p&gt;However, can the fact that a line of code is called by a test guarantee that the covered logic is tested thoroughly? At this stage, you face the need for a qualitative assessment of tests rather than a quantitative one. &lt;/p&gt;

&lt;p&gt;The idea of Mutation Testing is to randomly change the tested code, creating different variants of behavior that were not anticipated by the developer - &lt;code&gt;mutants&lt;/code&gt;. Then, your unit tests  run over the different mutations: if a test fails - a mutant is detected (and &lt;code&gt;"killed"&lt;/code&gt; in terms of MT) and the test is &lt;code&gt;strong&lt;/code&gt; enough, if not (a mutant &lt;code&gt;"survived"&lt;/code&gt;) - the test does not cover all possible variants of program behavior and is potentially vulnerable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pitest
&lt;/h2&gt;

&lt;p&gt;The Mutation Testing concept became popular quite many years ago and now it is implemented in several frameworks. One of the most successful and popular is Pitest.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk3q3o1elrgm5ldrxiqx6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk3q3o1elrgm5ldrxiqx6.png" alt="Comparison of Java MT systems (from https://pitest.org/java_mutation_testing_systems/)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Comparison of Java MT systems (from &lt;a href="https://pitest.org/java_mutation_testing_systems/" rel="noopener noreferrer"&gt;here&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;To make it work all you need is to have a code and some tests over it and add Pitest dependencies to your maven or gradle config.&lt;/p&gt;

&lt;p&gt;During the execution the plugin:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Detects covered lines of code (there is no sense in mutating uncovered)&lt;/li&gt;
&lt;li&gt;Copies corresponding class files in memory (no changes in a working directory by default)&lt;/li&gt;
&lt;li&gt;Mutates some specific places in copies according to the configurable list of allowed mutators&lt;/li&gt;
&lt;li&gt;Runs your tests over them and provides a report in different formats &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Talking about changes in the source code we are considering some specific allowable list of mutators: there is no sense to change our code in a literally random way to make syntax or structural errors. But if you replace:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;plus&lt;/strong&gt; with a &lt;strong&gt;minus&lt;/strong&gt; in math expressions;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;true&lt;/strong&gt; with &lt;strong&gt;false&lt;/strong&gt; in logical;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;link&lt;/strong&gt; to an object with &lt;strong&gt;null&lt;/strong&gt; in return statement;&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;it may help you to test your test's quality safely.&lt;/p&gt;

&lt;p&gt;Full list of mutators implemented in Pitest you can find &lt;a href="https://pitest.org/quickstart/mutators/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practice! Let's write some code
&lt;/h2&gt;

&lt;p&gt;Let's create a simple maven project with only one class &lt;code&gt;\tools\IntComparator.java&lt;/code&gt;.&lt;br&gt;
Then we need to implement a single method that can receive two int arguments, compare them and return &lt;code&gt;"first"&lt;/code&gt; if the first is greater and &lt;code&gt;"second"&lt;/code&gt; otherwise:&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="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IntComparator&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"first"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"second"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&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;h2&gt;
  
  
  Write a test
&lt;/h2&gt;

&lt;p&gt;We will use JUnit for testing our application so we need to add a dependency to pom.xml:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.junit.jupiter&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;junit-jupiter-engine&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;5.8.1&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that let's create a test.&lt;br&gt;
Our testing scenarios are quite simple:&lt;br&gt;
1) 5 &amp;gt; 3 -&amp;gt; "first"&lt;br&gt;
2) 3 &amp;lt; 5 -&amp;gt; "second"&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="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.junit.jupiter.api.Test&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;junit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;jupiter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Assertions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IntComparatorTest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;IntComparator&lt;/span&gt; &lt;span class="n"&gt;intComparator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IntComparator&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;testMax&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"first"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;intComparator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"second"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;intComparator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;));&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;Now, if you run it you will see that test passes successfully. Moreover, if you calculate a code coverage it will be 100%.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqlde1h9lhzg8jptcprea.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqlde1h9lhzg8jptcprea.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Does it mean that all cases are covered? &lt;br&gt;
No.&lt;/p&gt;
&lt;h2&gt;
  
  
  Let's mutate!
&lt;/h2&gt;

&lt;p&gt;Add pitest dependency to the &lt;code&gt;dependencies&lt;/code&gt; section of pom.xml:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.pitest&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;pitest-parent&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.11.5&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;pom&lt;span class="nt"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and a plugin to &lt;code&gt;build&lt;/code&gt; section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.pitest&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;pitest-maven&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.11.5&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.pitest&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;pitest-junit5-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.1.1&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;targetClasses&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;param&amp;gt;&lt;/span&gt;tools.*&lt;span class="nt"&gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/targetClasses&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;targetTests&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;param&amp;gt;&lt;/span&gt;tools.*&lt;span class="nt"&gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/targetTests&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/build&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And after that, let's run our new plugin to start MT:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mvn clean verify pitest:mutationCoverage 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command generates two reports: in the text format to the console and additionally to an .html file in your target folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;================================================================================
- Mutators
================================================================================
&amp;gt; org.pitest.mutationtest.engine.gregor.mutators.ConditionalsBoundaryMutator
&amp;gt;&amp;gt; Generated 1 Killed 0 (0%)
&amp;gt; KILLED 0 SURVIVED 1 TIMED_OUT 0 NON_VIABLE 0 
&amp;gt; MEMORY_ERROR 0 NOT_STARTED 0 STARTED 0 RUN_ERROR 0 
&amp;gt; NO_COVERAGE 0 
--------------------------------------------------------------------------------
&amp;gt; org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator
&amp;gt;&amp;gt; Generated 2 Killed 2 (100%)
&amp;gt; KILLED 2 SURVIVED 0 TIMED_OUT 0 NON_VIABLE 0 
&amp;gt; MEMORY_ERROR 0 NOT_STARTED 0 STARTED 0 RUN_ERROR 0 
&amp;gt; NO_COVERAGE 0 
--------------------------------------------------------------------------------
&amp;gt; org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator
&amp;gt;&amp;gt; Generated 1 Killed 1 (100%)
&amp;gt; KILLED 1 SURVIVED 0 TIMED_OUT 0 NON_VIABLE 0 
&amp;gt; MEMORY_ERROR 0 NOT_STARTED 0 STARTED 0 RUN_ERROR 0 
&amp;gt; NO_COVERAGE 0 
--------------------------------------------------------------------------------
================================================================================
- Timings
================================================================================
&amp;gt; pre-scan for mutations : &amp;lt; 1 second
&amp;gt; scan classpath : &amp;lt; 1 second
&amp;gt; coverage and dependency analysis : &amp;lt; 1 second
&amp;gt; build mutation tests : &amp;lt; 1 second
&amp;gt; run mutation analysis : &amp;lt; 1 second
--------------------------------------------------------------------------------
&amp;gt; Total  : &amp;lt; 1 second
--------------------------------------------------------------------------------
================================================================================
- Statistics
================================================================================
&amp;gt;&amp;gt; Line Coverage (for mutated classes only): 4/4 (100%)
&amp;gt;&amp;gt; Generated 4 mutations Killed 3 (75%)
&amp;gt;&amp;gt; Mutations with no coverage 0. Test strength 75%
&amp;gt;&amp;gt; Ran 4 tests (1 tests per mutation)
Enhanced functionality available at https://www.arcmutate.com/
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we can see a list of mutators that were used to test our code: mutants provided by &lt;code&gt;NegateConditionalsMutator&lt;/code&gt; and &lt;code&gt;EmptyObjectReturnValsMutator&lt;/code&gt; were successfully killed. &lt;br&gt;
It means that Pitest replaced &lt;code&gt;&amp;gt;&lt;/code&gt; to &lt;code&gt;&amp;lt;&lt;/code&gt; in line 6 of IntComparator.java and the first assertion in our test failed because the mutant returns &lt;code&gt;"second"&lt;/code&gt; instead of &lt;code&gt;"first"&lt;/code&gt;. &lt;br&gt;
Or, for another mutator, the code returns &lt;code&gt;""&lt;/code&gt; instead of the expected string and also was detected and killed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzvp0ix9fvsadij7ttyfb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzvp0ix9fvsadij7ttyfb.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the .html report we can also find a list of active mutators.&lt;/p&gt;

&lt;p&gt;As for the failed test generated by &lt;code&gt;ConditionalsBoundaryMutator&lt;/code&gt;, this mutator changes condition boundaries. &lt;br&gt;
The condition was changed from &lt;code&gt;a &amp;gt; b&lt;/code&gt; to &lt;code&gt;a &amp;gt;= b&lt;/code&gt;. That mutant survived because in the test we don't check an expected result for the case when &lt;code&gt;a == b&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Kill the mutant!
&lt;/h2&gt;

&lt;p&gt;Simply add missing assertion to the unit test:&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="nd"&gt;@Test&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;testMax&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"first"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;intComparator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"second"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;intComparator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"second"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;intComparator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&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;And re-run pitest plugin:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F18sf7tuyuqevduowinkn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F18sf7tuyuqevduowinkn.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great! MT clearly found the vulnerability and helped our test become better. So why hasn't the use of MT become an industry standard yet?&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-life problems
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;False positives&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm going to change an example above just a little bit to demonstrate a problem.&lt;br&gt;
From now the tested method will return &lt;code&gt;int&lt;/code&gt; result instead of &lt;code&gt;String&lt;/code&gt;. At the same time, we will keep all assertions in the test.&lt;/p&gt;

&lt;p&gt;IntComparator:&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="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;;&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;IntComparatorTest:&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="nd"&gt;@Test&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;testMax&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;intComparator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;intComparator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;intComparator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&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;Check the results:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fprf4mx2l7o686pazrf3d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fprf4mx2l7o686pazrf3d.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What happened? All the cases were covered! But we again have a mutant that survived. There is no matter for the test which value was returned &lt;code&gt;a&lt;/code&gt; or &lt;code&gt;b&lt;/code&gt; because it is just a primitive value.&lt;br&gt;
This is a typical example of a false positive result when the test is stricter than you really need. Searching through the internet you will find that it is quite a popular complaint.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7jg0cuecyw54hynb62ue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7jg0cuecyw54hynb62ue.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Mutation Testing is not fast. This is not a secret and there is no need to have illusions about it. Pitest states directly on its landing page that it is fast, but immediately explains what "fast" means in this context. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsegj2yfhm9azu9mkidds.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsegj2yfhm9azu9mkidds.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By experimenting with different sets of mutators, using several threads or thanks to optimizations within the engine (for example, &lt;a href="https://blog.pitest.org/less-is-more/" rel="noopener noreferrer"&gt;avoiding the creation of useless subsumed mutants&lt;/a&gt;), it is possible to achieve a decent reduction in the time it takes to perform mutationion testing. However, it will still be significant and will add minutes or tens of minutes to the testing process.&lt;/p&gt;

&lt;p&gt;I also recommend &lt;a href="https://nexocode.com/blog/posts/mutation-testing/" rel="noopener noreferrer"&gt;this post&lt;/a&gt; about the real-life experience of MT and performance analysis in particular.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recommended way of usage
&lt;/h2&gt;

&lt;p&gt;In Dec 2016, Java Magazine published an article by Henry Coles, the author of Pitest, titled "Mutation Testing: Automate the Search for Imperfect Tests". In the article, he discusses the possibility of using MT on real and large projects and, to sum up the main idea in one sentence, it would be: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The only code you need to perform mutation testing on is code that you've just written or changed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then this idea was developed and described in the article "&lt;a href="https://blog.pitest.org/dont-let-your-code-dry/" rel="noopener noreferrer"&gt;Don't let your code dry&lt;/a&gt;", which is now posted on the Pitest blog.&lt;/p&gt;

&lt;p&gt;Speaking more about implementation in the context of Pitest, the author explains how incremental testing can be practically implemented: using the local plugin launch (which, by the way, is integrated with GIT and can only access files that have been added or changed) or by running MT during pull request analysis.&lt;/p&gt;

&lt;p&gt;Thus, it can be said that adding mutation testing to the entire project's CI pipeline is probably not the best idea. On the other hand, using an incremental approach will minimize the impact of the problem factors described above and make mutation testing a more interesting instrument.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Mutation Testing is not a silver bullet!&lt;br&gt;
We use the word &lt;code&gt;strong&lt;/code&gt; to describe tests that achieve high mutation coverage because they are able to detect and kill most mutants, indicating that they are effective at catching potential bugs in the code.&lt;/p&gt;

&lt;p&gt;However, even if tests are strong, it does not mean that they are good. Will they pass if the implementation changes but the behavior is kept the same? Do they &lt;a href="https://testsmells.org/pages/testsmells.html" rel="noopener noreferrer"&gt;smell&lt;/a&gt;? Are they quick, and do not impose unnecessary overhead?&lt;/p&gt;

&lt;p&gt;Overall, while strong tests are a good indicator of test effectiveness, other factors should be considered to ensure that the tests are reliable and comprehensive.&lt;/p&gt;

&lt;p&gt;My personal problem with regard to Mutation Testing turned out to be inflated expectations. The concept itself seems very exciting, but the more you dive into the details, the more you notice limitations. The main one, which is always on the surface, is that testing tools do not know the logic of your application, and no matter what variability or prediction methods they use, they only provide you with results for analysis. The final decision on the quality of tests is up to the developer or the team. And Mutation Testing will work effectively as long as you are willing to pay attention to its reports and process each case it identifies.&lt;/p&gt;

&lt;p&gt;Would I use MT? For small projects based on algorithmic calculations, such as a core library for some product that requires high code quality - absolutely yes. For projects where quality requirements are not so critical - probably not.&lt;/p&gt;

&lt;p&gt;In any case, I will closely monitor the evolution of this idea and in particular the Pitest project. Already now, it is a developed product integrated with GIT, Eclipse, IntelliJ, Sonar, etc. I am also inspired by the level of activity and support for the project from its author and community. &lt;/p&gt;

&lt;p&gt;P.S.&lt;/p&gt;

&lt;p&gt;Many are expecting the arrival of AI in development. Solutions that understand context and business logic will take analysis and testing tools to a new level. I think that Pitest is one of those projects that could unexpectedly evolve and become much more popular in the near future. I'm looking forward to it.&lt;/p&gt;

</description>
      <category>java</category>
      <category>testing</category>
    </item>
    <item>
      <title>[TIL] Helm: Quickstart guide</title>
      <dc:creator>force_push</dc:creator>
      <pubDate>Mon, 19 Dec 2022 21:01:55 +0000</pubDate>
      <link>https://dev.to/force_push/til-helm-quickstart-guide-3dhd</link>
      <guid>https://dev.to/force_push/til-helm-quickstart-guide-3dhd</guid>
      <description>&lt;p&gt;This topic was created in &lt;code&gt;#todayilearned&lt;/code&gt; style as a quickstart guide for beginners. When you finish it you will understand what Helm is, what it can do, how to use it and what you can learn next. We will discuss &lt;code&gt;Helm 3&lt;/code&gt; and contrast it with &lt;code&gt;Helm 2&lt;/code&gt; where they differ in interesting ways. &lt;br&gt;
The article contains a practical part, so it is expected that you're familiar with Linux command line, basic Kubernetes abstractions (service, deployment, pod, configmap, secret) and kubectl tool.  &lt;/p&gt;

&lt;p&gt;However, let's start with some theory and the problem statement in particular.  &lt;/p&gt;
&lt;h2&gt;
  
  
  How to deploy to a cluster?
&lt;/h2&gt;

&lt;p&gt;Natively, the simplest way is to create some .yaml files for required Kubernetes abstractions and deploy them using &lt;code&gt;apply&lt;/code&gt; command.&lt;br&gt;&lt;br&gt;
It works when the amount of abstractions is not big, but in real life even one service can be represented by several abstractions (deployment, configmaps, service, ingress, secrets, persistent volume, etc). When we talk about huge projects, the number of abstractions may exceed several thousands. It becomes almost impossible to support it manually. Just imagine that you need to edit a label in all manifests. &lt;/p&gt;

&lt;p&gt;Of course, we can use &lt;code&gt;sed&lt;/code&gt; or &lt;code&gt;envsubst&lt;/code&gt; or another tool for batch file editing and it will work. &lt;/p&gt;

&lt;p&gt;We can go even further and use Kubernetes module in &lt;code&gt;Ansible&lt;/code&gt;, &lt;code&gt;Kustomize&lt;/code&gt; or &lt;code&gt;Jsonnet&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;Talking about &lt;code&gt;Ansible&lt;/code&gt;, it uses &lt;code&gt;Jinja2&lt;/code&gt; as a template language and all customization is based on environment variables. &lt;code&gt;Kustomize&lt;/code&gt; and &lt;code&gt;Jsonnet&lt;/code&gt; both operate wit configuration files.  &lt;/p&gt;

&lt;p&gt;In other words, all these instruments have a similar core concept:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set of application manifests (and they can be templated: contain placeholders instead of values)
&lt;/li&gt;
&lt;li&gt;Config file (values stored here)
&lt;/li&gt;
&lt;li&gt;Way to deploy to a cluster
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, all the instruments mentioned are kubectl-based. It means that after some updates, we will need to apply changes using kubectl. &lt;/p&gt;
&lt;h2&gt;
  
  
  What if we need to roll back?
&lt;/h2&gt;

&lt;p&gt;Using kubectl only, we're limited with the command &lt;code&gt;kubectl rollout undo&lt;/code&gt; and it works in a pretty restricted way: it can rollback only a deployment's revision (but not service, ingress, etc).&lt;/p&gt;

&lt;p&gt;We didn't just mention at the very beginning a possibility to apply changes to abstractions like right from files. For now, we can solve the problem of rollback using it. What if we archive the previous version of all manifests and save it before doing some updates and reapply them in case a rollback is needed? &lt;strong&gt;Viola! This is what Helm actually does&lt;/strong&gt; 😊 &lt;/p&gt;

&lt;p&gt;Helm literally gets the manifests for the current release and saves them to the secrets (Base 64 encoded archive).&lt;/p&gt;
&lt;h2&gt;
  
  
  Alternative
&lt;/h2&gt;

&lt;p&gt;For years, Helm has been the industry standard (or some Helm-based tools). But there is another thing you need to pay attention to. &lt;code&gt;GitOps&lt;/code&gt; is a concept based on synchronization between the master branch and the cluster state. One of the most popular DevOps tools at the moment is ArgoCD. This concept is becoming popular rapidly, so keep your finger on the pulse!&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Helm?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Helm is a package manager and it supports rollbacks (Kustomize and other don't)&lt;/li&gt;
&lt;li&gt;Cloud Native Computing Foundation supports Helm. As part of the Linux Foundation, CNCF provide support, oversight and direction for fast-growing, cloud native projects, including Kubernetes and Prometheus&lt;/li&gt;
&lt;li&gt;Helm provides declarative and idempotent commands&lt;/li&gt;
&lt;li&gt;Helm has important things for CI/CD:

&lt;ul&gt;
&lt;li&gt;Watch (tracks status of deployments, wait for timeout)
&lt;/li&gt;
&lt;li&gt;Rollback (auto in case of error)
&lt;/li&gt;
&lt;li&gt;Hooks (ability to deploy manifests in exact order) &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Helm supports plugins &lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Charts
&lt;/h2&gt;

&lt;p&gt;Helm is a package manager, and in terms of a Helm package has a special name – &lt;code&gt;chart&lt;/code&gt;. &lt;br&gt;
What is a chart? It is just a .tgz archive, which contains: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Batch of templated manifests &lt;/li&gt;
&lt;li&gt;File with values &lt;/li&gt;
&lt;li&gt;Metadata (version, developed by, etc)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Go template syntax
&lt;/h2&gt;

&lt;p&gt;Helm uses Go template syntax in manifests (&lt;a href="https://helm.sh/docs/chart_template_guide/functions_and_pipelines/" rel="noopener noreferrer"&gt;see more&lt;/a&gt;). &lt;br&gt;
Look at the example:&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;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ConfigMap&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Release.Name&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;-configmap&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myvalue&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;World"&lt;/span&gt;
  &lt;span class="na"&gt;drink&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;quote .Values.favorite.drink&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
  &lt;span class="na"&gt;food&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;quote .Values.favorite.food&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the last string, we try to get the value for the field &lt;code&gt;food&lt;/code&gt; from Helm's variable (described in the file or command line). Obviously, variables can also have some nested levels, so we use dots for navigating deeper through the structure.  &lt;/p&gt;

&lt;p&gt;A default file with values usually has name &lt;code&gt;values.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Helm can do?
&lt;/h2&gt;

&lt;p&gt;As a package management tool, Helm does the following: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create new charts from scratch &lt;/li&gt;
&lt;li&gt;Package charts into chart archives (.tgz files) &lt;/li&gt;
&lt;li&gt;Interact with chart repositories where charts are stored &lt;/li&gt;
&lt;li&gt;Install and uninstall charts into an existing Kubernetes cluster &lt;/li&gt;
&lt;li&gt;Manage the release cycle of charts that have been installed with Helm &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Basic Helm commands logically evolved from the goals above: &lt;/p&gt;

&lt;p&gt;&lt;em&gt;helm create&lt;/em&gt; – create a new chart&lt;br&gt;
&lt;em&gt;helm search&lt;/em&gt; – search for a chart&lt;br&gt;
&lt;em&gt;helm install&lt;/em&gt; – install chart&lt;br&gt;
&lt;em&gt;helm upgrade&lt;/em&gt; – upgrade chart&lt;br&gt;
&lt;em&gt;helm pull&lt;/em&gt; – download chart&lt;br&gt;
&lt;em&gt;helm show&lt;/em&gt; – show the information about chart&lt;br&gt;
&lt;em&gt;helm list&lt;/em&gt; – show list of installed charts&lt;br&gt;
&lt;em&gt;helm uninstall&lt;/em&gt; – uninstall chart&lt;br&gt;
&lt;em&gt;etc&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Practical task
&lt;/h2&gt;

&lt;p&gt;It's time to practice!  &lt;/p&gt;

&lt;p&gt;Starting from here, I'd like to offer to setup the environment, create our own chart and do several primitive actions with it. &lt;/p&gt;

&lt;p&gt;During the practice, I'll try to cover basic commands, flags, scenarios of usage and pitfalls, share some useful links, etc. &lt;/p&gt;

&lt;p&gt;What are we going to do? &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setup environment: Microk8s and its addons &lt;/li&gt;
&lt;li&gt;Create new chart: &lt;code&gt;create&lt;/code&gt; command and chart structure&lt;/li&gt;
&lt;li&gt;Configure new chart: &lt;code&gt;values.yaml&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Pre-install steps: &lt;code&gt;lint&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Pre-install steps: &lt;code&gt;--dry-run&lt;/code&gt; and &lt;code&gt;template&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Deploy chart to Kubernetes: &lt;code&gt;install&lt;/code&gt; command &lt;/li&gt;
&lt;li&gt;Run tests: &lt;code&gt;test&lt;/code&gt; command &lt;/li&gt;
&lt;li&gt;Upgrade chart: adding new features, &lt;code&gt;upgrade&lt;/code&gt; command &lt;/li&gt;
&lt;li&gt;Do changes: &lt;code&gt;--set&lt;/code&gt; attribute, changes in ConfigMaps, rollout &lt;/li&gt;
&lt;li&gt;Rollback changes: &lt;code&gt;history&lt;/code&gt;, &lt;code&gt;rollback&lt;/code&gt;, how to work with revisions&lt;/li&gt;
&lt;li&gt;Prepare to distribution: &lt;code&gt;package&lt;/code&gt; command &lt;/li&gt;
&lt;li&gt;Repositories: local, external, registries &lt;/li&gt;
&lt;li&gt;Install chart from an external repository
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Setup environment: Microk8s and it's addons
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Microk8s&lt;/code&gt; - is the simplest production-grade upstream K8s. Lightweight and focused. Single command install on Linux.&lt;br&gt;
Run from the terminal to install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo snap install microk8s --classic 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Kubectl&lt;/code&gt; - Microk8s comes with its own packaged version of the kubectl command for operating Kubernetes. By default, this is accessed through Microk8s, to avoid interfering with any version which may already be on your host machine.&lt;br&gt;
We will use embedded kubectl like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;microk8s kubectl 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Helm&lt;/code&gt; - Microk8s supports addons including Helm. There are two addons in default repository: &lt;code&gt;helm&lt;/code&gt; alias stands for Helm 2 and &lt;code&gt;helm3&lt;/code&gt; – stands for Helm 3. &lt;a href="https://helm.sh/blog/helm-v2-deprecation-timeline/" rel="noopener noreferrer"&gt;Helm 2 support ended in 2020&lt;/a&gt;, so we will use helm3. If you're interested in differences between Helm versions, please take a look at this straightforward and informative &lt;a href="https://blog.knoldus.com/helm2-vs-helm3-simplified/" rel="noopener noreferrer"&gt;article&lt;/a&gt;.&lt;br&gt;
Run next commands from the terminal to prepare Helm addon:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;microk8s enable helm3 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;microk8s helm3 init 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create new chart: &lt;code&gt;create&lt;/code&gt; command and chart structure
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;create&lt;/code&gt; command is used to generate basic chart's structure from scratch. Of cause, you can create it manually, so the command just make this process easier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s helm3 create mychart 
Creating mychart 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a result we have a new folder &lt;code&gt;mychart&lt;/code&gt; generated with other folders and files inside. This is the typical structure for a Helm chart; almost all charts you'll meet have the same.&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;./mychart/&lt;/span&gt; 

&lt;span class="s"&gt;├── charts&lt;/span&gt; &lt;span class="c1"&gt;# A directory containing any charts upon which this chart depends.&lt;/span&gt;
&lt;span class="s"&gt;├── Chart.yaml&lt;/span&gt; &lt;span class="c1"&gt;# A YAML file containing information about the chart: name, description, version, dependencies, etc. Required for a chart.&lt;/span&gt;
&lt;span class="s"&gt;├── templates&lt;/span&gt; &lt;span class="c1"&gt;# A directory of templates that, when combined with values, will generate valid Kubernetes manifest files.&lt;/span&gt;
&lt;span class="s"&gt;│   ├── deployment.yaml&lt;/span&gt; &lt;span class="c1"&gt;# Deployment manifest.&lt;/span&gt;
&lt;span class="s"&gt;│   ├── _helpers.tpl&lt;/span&gt; &lt;span class="c1"&gt;# Contains a set of rules about naming and versioning for the chart and k8s abstractions. Can be customized for your purposes.&lt;/span&gt;
&lt;span class="s"&gt;│   ├── ingress.yaml&lt;/span&gt; &lt;span class="c1"&gt;# Ingress manifest.&lt;/span&gt;
&lt;span class="s"&gt;│   ├── NOTES.txt&lt;/span&gt; &lt;span class="c1"&gt;# A plain text file containing short usage notes. Will be printed in the console after the installation.&lt;/span&gt;
&lt;span class="s"&gt;│   ├── serviceaccount.yaml&lt;/span&gt; &lt;span class="c1"&gt;# Serviceaccount manifest.&lt;/span&gt;
&lt;span class="s"&gt;│   ├── service.yaml&lt;/span&gt; &lt;span class="c1"&gt;# Service manifest.&lt;/span&gt;
&lt;span class="s"&gt;│   └── tests&lt;/span&gt; &lt;span class="c1"&gt;# A directory containing tests that validate that your chart works as expected when it is installed.&lt;/span&gt;
&lt;span class="s"&gt;│       └── test-connection.yaml&lt;/span&gt; &lt;span class="c1"&gt;# Test sample does wget to the chart's host:port.&lt;/span&gt;
&lt;span class="s"&gt;└── values.yaml&lt;/span&gt; &lt;span class="c1"&gt;# A file containing values for parametrizing templates.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configure new chart: &lt;code&gt;values.yaml&lt;/code&gt; file
&lt;/h2&gt;

&lt;p&gt;The service we're going to build needs to be quite simple, so make it a single &lt;code&gt;nginx&lt;/code&gt; replica that can respond with a welcome page upon request.&lt;/p&gt;

&lt;p&gt;The manifest files contain Go templates that will be replaced by some real data from the values.yaml file. As a result, we must update the &lt;code&gt;values.yaml&lt;/code&gt; file in order to customize our Helm chart.  &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;values.yaml&lt;/code&gt; file by default looks 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="c1"&gt;# Default values for test1. &lt;/span&gt;
&lt;span class="c1"&gt;# This is a YAML-formatted file. &lt;/span&gt;
&lt;span class="c1"&gt;# Declare variables to be passed into your templates. &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;image&lt;/span&gt;&lt;span class="pi"&gt;:&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;nginx&lt;/span&gt; 
  &lt;span class="na"&gt;pullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IfNotPresent&lt;/span&gt; 
  &lt;span class="c1"&gt;# Overrides the image tag whose default is the chart appVersion. &lt;/span&gt;
  &lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; 

&lt;span class="na"&gt;imagePullSecrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt; 

&lt;span class="na"&gt;nameOverride&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; 

&lt;span class="na"&gt;fullnameOverride&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; 

&lt;span class="na"&gt;serviceAccount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="c1"&gt;# Specifies whether a service account should be created &lt;/span&gt;
  &lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; 
  &lt;span class="c1"&gt;# Annotations to add to the service account &lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt; 
  &lt;span class="c1"&gt;# The name of the service account to use. &lt;/span&gt;
  &lt;span class="c1"&gt;# If not set and create is true, a name is generated using the fullname template &lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; 

&lt;span class="na"&gt;podAnnotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt; 

&lt;span class="na"&gt;podSecurityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt; 
  &lt;span class="c1"&gt;# fsGroup: 2000 &lt;/span&gt;

&lt;span class="na"&gt;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt; 
  &lt;span class="c1"&gt;# capabilities: &lt;/span&gt;
  &lt;span class="c1"&gt;#   drop: &lt;/span&gt;
  &lt;span class="c1"&gt;#   - ALL &lt;/span&gt;
  &lt;span class="c1"&gt;# readOnlyRootFilesystem: true &lt;/span&gt;
  &lt;span class="c1"&gt;# runAsNonRoot: true &lt;/span&gt;
  &lt;span class="c1"&gt;# runAsUser: 1000 &lt;/span&gt;

&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt; 
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;80&lt;/span&gt; 

&lt;span class="na"&gt;ingress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; 
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt; 
    &lt;span class="c1"&gt;# kubernetes.io/ingress.class: nginx &lt;/span&gt;
    &lt;span class="c1"&gt;# kubernetes.io/tls-acme: "true" &lt;/span&gt;
  &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;chart-example.local&lt;/span&gt; 
      &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt; 
  &lt;span class="na"&gt;tls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt; 
  &lt;span class="c1"&gt;#  - secretName: chart-example-tls &lt;/span&gt;
  &lt;span class="c1"&gt;#    hosts: &lt;/span&gt;
  &lt;span class="c1"&gt;#      - chart-example.local &lt;/span&gt;

&lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt; 
  &lt;span class="c1"&gt;# We usually recommend not to specify default resources and to leave this as a conscious &lt;/span&gt;
  &lt;span class="c1"&gt;# choice for the user. This also increases chances charts run on environments with little &lt;/span&gt;
  &lt;span class="c1"&gt;# resources, such as Minikube. If you do want to specify resources, uncomment the following &lt;/span&gt;
  &lt;span class="c1"&gt;# lines, adjust them as necessary, and remove the curly braces after 'resources:'. &lt;/span&gt;
  &lt;span class="c1"&gt;# limits: &lt;/span&gt;
  &lt;span class="c1"&gt;#   cpu: 100m &lt;/span&gt;
  &lt;span class="c1"&gt;#   memory: 128Mi &lt;/span&gt;
  &lt;span class="c1"&gt;# requests: &lt;/span&gt;
  &lt;span class="c1"&gt;#   cpu: 100m &lt;/span&gt;
  &lt;span class="c1"&gt;#   memory: 128Mi &lt;/span&gt;

&lt;span class="na"&gt;autoscaling&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; 
  &lt;span class="na"&gt;minReplicas&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;maxReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;100&lt;/span&gt; 
  &lt;span class="na"&gt;targetCPUUtilizationPercentage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;80&lt;/span&gt; 
  &lt;span class="c1"&gt;# targetMemoryUtilizationPercentage: 80 &lt;/span&gt;

&lt;span class="na"&gt;nodeSelector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt; 

&lt;span class="na"&gt;tolerations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt; 

&lt;span class="na"&gt;affinity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt; 

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All sections (top-level properties) are pretty self-descriptive and have the same names as the main properties of Kubernetes abstractions, so I will not describe each of them in detail and will stay on the changes which we need to do here. &lt;/p&gt;

&lt;p&gt;To avoid overcomplication I won't adjust ingress params, settings related to security, autoscaling, pods tolerations, and affinity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common properties&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the top section, we will change only &lt;code&gt;image.pullPolicy&lt;/code&gt;, &lt;code&gt;nameOverride&lt;/code&gt; and &lt;code&gt;fullnameOverride&lt;/code&gt;: &lt;/p&gt;

&lt;p&gt;Before:&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;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;image&lt;/span&gt;&lt;span class="pi"&gt;:&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;nginx&lt;/span&gt; 
  &lt;span class="na"&gt;pullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IfNotPresent&lt;/span&gt; 
  &lt;span class="c1"&gt;# Overrides the image tag whose default is the chart appVersion. &lt;/span&gt;
  &lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; 

&lt;span class="na"&gt;imagePullSecrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt; 
&lt;span class="na"&gt;nameOverride&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; 
&lt;span class="na"&gt;fullnameOverride&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After:&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;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;image&lt;/span&gt;&lt;span class="pi"&gt;:&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;nginx&lt;/span&gt; 
  &lt;span class="na"&gt;pullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Always&lt;/span&gt; 
  &lt;span class="c1"&gt;# Overrides the image tag whose default is the chart appVersion. &lt;/span&gt;
  &lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; 

&lt;span class="na"&gt;imagePullSecrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt; 
&lt;span class="na"&gt;nameOverride&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;helm-hw"&lt;/span&gt; 
&lt;span class="na"&gt;fullnameOverride&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;helm-hw"&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pullPolicy&lt;/code&gt; has several options to setup: &lt;code&gt;IfNotPresent&lt;/code&gt;, &lt;code&gt;Latest&lt;/code&gt;, and &lt;code&gt;Always&lt;/code&gt;. I'd like to use &lt;code&gt;Always&lt;/code&gt; here just to avoid any issues with image failure. &lt;br&gt;
Properties &lt;code&gt;nameOverride&lt;/code&gt; and &lt;code&gt;fullnameOverride&lt;/code&gt; will be used for naming a chart and Kubernetes abstractions according to the rules described in &lt;code&gt;templtes/_helpers.tpl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Service account properties&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Service account provides a user identity to run in the pod inside the cluster. If it's left empty, Helm will generate the name according to &lt;code&gt;templtes/_helpers.tpl&lt;/code&gt; rules based on the full name. It is a good practice to have a &lt;code&gt;serviceAccount&lt;/code&gt; set up so that the application will be directly associated with a user that is controlled in the chart.&lt;/p&gt;

&lt;p&gt;Before:&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;serviceAccount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="c1"&gt;# Specifies whether a service account should be created &lt;/span&gt;
  &lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; 
  &lt;span class="c1"&gt;# Annotations to add to the service account &lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt; 
  &lt;span class="c1"&gt;# The name of the service account to use. &lt;/span&gt;
  &lt;span class="c1"&gt;# If not set and create is true, a name is generated using the fullname template &lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After:&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;serviceAccount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="c1"&gt;# Specifies whether a service account should be created &lt;/span&gt;
  &lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; 
  &lt;span class="c1"&gt;# Annotations to add to the service account &lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt; 
  &lt;span class="c1"&gt;# The name of the service account to use. &lt;/span&gt;
  &lt;span class="c1"&gt;# If not set and create is true, a name is generated using the fullname template &lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test"&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Service properties&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here we will change only &lt;code&gt;service.type&lt;/code&gt;: &lt;/p&gt;

&lt;p&gt;Before:&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;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt; 
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;80&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After:&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;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NodePort&lt;/span&gt; 
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;80&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are only two possible options and when &lt;code&gt;ClusterIP&lt;/code&gt; type allows access only from inside the cluster or through the ingress (which we will leave disabled) &lt;code&gt;NodePort&lt;/code&gt; exposes access to the service through a statically assigned port.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resource properties&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Optional, but it is always a good practice to setup resource settings. Nobody knows how many resources consume your service better than you!&lt;/p&gt;

&lt;p&gt;Before:&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;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt; 
  &lt;span class="c1"&gt;# We usually recommend not to specify default resources and to leave this as a conscious &lt;/span&gt;
  &lt;span class="c1"&gt;# choice for the user. This also increases chances charts run on environments with little &lt;/span&gt;
  &lt;span class="c1"&gt;# resources, such as Minikube. If you do want to specify resources, uncomment the following &lt;/span&gt;
  &lt;span class="c1"&gt;# lines, adjust them as necessary, and remove the curly braces after 'resources:'. &lt;/span&gt;
  &lt;span class="c1"&gt;# limits: &lt;/span&gt;
  &lt;span class="c1"&gt;#   cpu: 100m &lt;/span&gt;
  &lt;span class="c1"&gt;#   memory: 128Mi &lt;/span&gt;
  &lt;span class="c1"&gt;# requests: &lt;/span&gt;
  &lt;span class="c1"&gt;#   cpu: 100m &lt;/span&gt;
  &lt;span class="c1"&gt;#   memory: 128Mi &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After:&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;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="c1"&gt;# We usually recommend not to specify default resources and to leave this as a conscious &lt;/span&gt;
  &lt;span class="c1"&gt;# choice for the user. This also increases chances charts run on environments with little &lt;/span&gt;
  &lt;span class="c1"&gt;# resources, such as Minikube. If you do want to specify resources, uncomment the following &lt;/span&gt;
  &lt;span class="c1"&gt;# lines, adjust them as necessary, and remove the curly braces after 'resources:'. &lt;/span&gt;
   &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&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;100m&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;128Mi&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;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;100m&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;128Mi&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Pre-install steps: &lt;code&gt;lint&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Helm contains an embedded linter - your go-to tool for verifying that your chart follows best practices. Before installing new or updating charts you can always check syntax and chart structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s helm3 lint ./mychart 
==&amp;gt; Linting ./mychart 
[INFO] Chart.yaml: icon is recommended 

1 chart(s) linted, 0 chart(s) failed 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I will remove "name" from Chart.yaml to demonstrate how linter finds an error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s helm3 lint ./mychart 
==&amp;gt; Linting ./mychart 
[ERROR] Chart.yaml: name is required 
[INFO] Chart.yaml: icon is recommended 
[ERROR] templates/: validation: chart.metadata.name is required 
[ERROR] : unable to load chart 
validation: chart.metadata.name is required 

Error: 1 chart(s) linted, 1 chart(s) failed 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I didn't find the default ruleset, so I can only refer you to the corresponding &lt;a href="https://github.com/helm/helm/tree/main/pkg/lint/rules" rel="noopener noreferrer"&gt;package in Helm's GitHub&lt;/a&gt; repository where linting rules are implemented. Historically, there were several request proposals for implementing customizable linting and adding custom rulesets, but they were not implemented, and in case you're looking for something like that I recommend paying attention to 3rd party plugins and projects like &lt;a href="https://github.com/helm/chart-testing" rel="noopener noreferrer"&gt;Chart Testing&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pre-install steps: &lt;code&gt;--dry-run&lt;/code&gt; and &lt;code&gt;template&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Except for linting, it would be great to do one more pre-install step – to parametrize the manifests and render them to validate what we are going to deploy to the cluster. &lt;/p&gt;

&lt;p&gt;Out of the box Helm provides two options for how to do it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dry-run&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;--dry-run&lt;/code&gt; flag with &lt;code&gt;install&lt;/code&gt; command Helm sends parametrized manifests to K8s API server, Kubernetes validates it and returns the resulting manifest file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;microk8s helm3 install mychart ./mychart --dry-run 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fizrw6pxddz21u1sscxn9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fizrw6pxddz21u1sscxn9.png" alt="Helm dry-run flow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Template&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;template&lt;/code&gt; command Helm doesn't call Kubernetes cluster. It handles all validations and rendering tasks by itself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;microk8s helm3 template ./mychart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5j43h6vbo21xdadqzt6h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5j43h6vbo21xdadqzt6h.png" alt="Helm template flow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;FYI: To print already installed manifests you can use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;microk8s helm3 get manifest [CHART_NAME]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploy chart to Kubernetes: &lt;code&gt;install&lt;/code&gt; command
&lt;/h2&gt;

&lt;p&gt;When all pre-install checks are passed we can install our chart to the cluster using &lt;code&gt;install&lt;/code&gt; command. It receives a chart name and source directory address as arguments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s helm3 install mychart ./mychart 
NAME: mychart 
LAST DEPLOYED: Wed Dec 01 00:00:59 2022 
NAMESPACE: default 
STATUS: deployed 
REVISION: 1 
NOTES: 
1. Get the application URL by running these commands: 
  export NODE_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports[0].nodePort}" services helm-hw) 
  export NODE_IP=$(kubectl get nodes --namespace default -o jsonpath="{.items[0].status.addresses[0].address}") 
  echo http://$NODE_IP:$NODE_PORT 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After successful installation, we can see usage notes from &lt;code&gt;NOTES.txt&lt;/code&gt; printed. By default, it explains how to get the installed chart's IP-address and port. Let's follow the recommendations to check if our nginx started.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s kubectl get nodes --namespace default -o jsonpath="{.items[0].status.addresses[0].address}" 

192.168.100.61 

root@helm-training# microk8s kubectl get --namespace default -o jsonpath="{.spec.ports[0].nodePort}" services helm-hw 

31880 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use this host:port combination to open nginx welcome page in a browser:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2isvutz04yy1qafd5ccn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2isvutz04yy1qafd5ccn.png" alt="Nginx default welcome page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To see all required Kubernetes abstractions created and started run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s kubectl get all 

NAME                           READY   STATUS    RESTARTS   AGE 
pod/helm-hw-55846c8758-lklsf   1/1     Running   0          12s 

NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE 
service/helm-hw      NodePort    10.152.183.75   &amp;lt;none&amp;gt;        80:31880/TCP   12s 

NAME                      READY   UP-TO-DATE   AVAILABLE   AGE 
deployment.apps/helm-hw   1/1     1            1           12s 

NAME                                 DESIRED   CURRENT   READY   AGE 
replicaset.apps/helm-hw-55846c8758   1         1         1       12s 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Run tests: &lt;code&gt;test&lt;/code&gt; command
&lt;/h2&gt;

&lt;p&gt;At this point, we have a perfect time to run tests. As you remember Helm is already generated one for us. It is quite simple and does nothing but &lt;code&gt;wget&lt;/code&gt; request to the chart's host:port, but let's try to run it and look for the report.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s helm3 test mychart 

NAME: mychart 
LAST DEPLOYED: Thu Dec 01 00:00:59 2022 
NAMESPACE: default 
STATUS: deployed 
REVISION: 1 
TEST SUITE:     helm-hw-test-connection 
Last Started:   Mon Dec 01 00:07:53 2022 
Last Completed: Mon Dec 01 00:08:34 2022 
Phase:          Succeeded 
NOTES: 
1. Get the application URL by running these commands: 
  export NODE_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports[0].nodePort}" services helm-hw) 
  export NODE_IP=$(kubectl get nodes --namespace default -o jsonpath="{.items[0].status.addresses[0].address}") 
  echo http://$NODE_IP:$NODE_PORT 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Warning: if the test fails with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Phase: Failed the problem is most likely in microk8s: kubelet does not have ClusterDNS IP configured by default&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Try to enable dns plugin like: &lt;/p&gt;



&lt;pre class="highlight plaintext"&gt;&lt;code&gt;microk8s helm3 enable dns 
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;and run the test again. &lt;/p&gt;


&lt;/blockquote&gt;

&lt;p&gt;This test created a new &lt;code&gt;pod/helm-hw-test-connection&lt;/code&gt; for its purposes and you can find it in &lt;code&gt;Completed&lt;/code&gt; state getting all pods from the current namespace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s kubectl get pods  

NAME                       READY   STATUS      RESTARTS   AGE 
helm-hw-55846c8758-lklsf   1/1     Running     0          7m51s 
helm-hw-test-connection    0/1     Completed   0          57s 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Feel free to delete this pod using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s kubectl delete pod helm-hw-test-connection 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Upgrade chart: adding new features, &lt;code&gt;upgrade&lt;/code&gt; command
&lt;/h2&gt;

&lt;p&gt;It's time to upgrade our chart and bring new functionality. Let's make nginx response with a custom message instead of the default html page. For that purpose, I am going to add a new ConfigMap template to the chart.  &lt;/p&gt;

&lt;p&gt;Let's create a new &lt;code&gt;configmap.yaml&lt;/code&gt; file under &lt;code&gt;templates&lt;/code&gt; directory with following content:&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;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt; 
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ConfigMap&lt;/span&gt; 
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-config&lt;/span&gt; 
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="na"&gt;nginx.conf&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt; 
&lt;span class="s"&gt;events&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{&lt;/span&gt; 
&lt;span class="s"&gt;}&lt;/span&gt; 
&lt;span class="s"&gt;http&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{&lt;/span&gt; 
   &lt;span class="s"&gt;server&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{&lt;/span&gt; 
       &lt;span class="s"&gt;listen&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;80;&lt;/span&gt; 
       &lt;span class="s"&gt;location&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{&lt;/span&gt; 
         &lt;span class="s"&gt;return&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;200&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;.Values.nginx.message&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Hello&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;world!"}};&lt;/span&gt; 
       &lt;span class="s"&gt;}&lt;/span&gt; 
   &lt;span class="s"&gt;}&lt;/span&gt; 
&lt;span class="s"&gt;}&lt;/span&gt; 
&lt;span class="s"&gt;'&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we used Go-template to tell Helm that will take the message from the &lt;code&gt;values.yaml&lt;/code&gt; or else use the default value "Hello world!". &lt;br&gt;
Now let's configure some custom message in &lt;code&gt;values.yaml&lt;/code&gt; adding the following yaml to the bottom:&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="c1"&gt;#...&lt;/span&gt;
&lt;span class="na"&gt;nginx&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Foo!"&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last change in templates files is to add the new ConfigMap to our Deployment. For doing that we will open &lt;code&gt;deployment.yaml&lt;/code&gt; and configure &lt;code&gt;template&lt;/code&gt; property:&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="c1"&gt;#...&lt;/span&gt;
&lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
    &lt;span class="c1"&gt;#... &lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="c1"&gt;#... &lt;/span&gt;
          &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;config-vol&lt;/span&gt; 
            &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/nginx/&lt;/span&gt; 
      &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;config-vol&lt;/span&gt; 
          &lt;span class="na"&gt;configMap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-config&lt;/span&gt; 
            &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx.conf&lt;/span&gt; 
                &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx.conf&lt;/span&gt; 
&lt;span class="c1"&gt;#...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep all data as is but add a new volume under &lt;code&gt;template&lt;/code&gt; parameter and new &lt;code&gt;volumeMount&lt;/code&gt; under &lt;code&gt;containers&lt;/code&gt;. This is how we tell Kubernetes to rewrite &lt;code&gt;/etc/nginx/nginx.conf&lt;/code&gt; file which is placed in nginx pod's file system to data from our config-map. &lt;/p&gt;

&lt;p&gt;Time to do pre-install checks to validate changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s helm3 lint ./mychart 

==&amp;gt; Linting ./mychart 
[INFO] Chart.yaml: icon is recommended 

1 chart(s) linted, 0 chart(s) failed 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s helm3 template ./mychart 

... 
# Source: mychart/templates/configmap.yaml 
apiVersion: v1 
kind: ConfigMap 
metadata: 
  name: nginx-config 
data: 
  nginx.conf: ' 
events { 
} 
http { 
   server { 
       listen 80; 
       location / { 
         return 200 Foo!; 
       } 
   } 
} 
' 
... 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that when we ask Helm to generate a manifest using &lt;code&gt;template&lt;/code&gt; command it provided us new ConfigMap with a custom message value. Try to remove the value from &lt;code&gt;values.yaml&lt;/code&gt; and run the command again and you will see the default message. &lt;/p&gt;

&lt;p&gt;The next command will &lt;code&gt;upgrade&lt;/code&gt; our chart and install the new version to the cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s helm3 upgrade mychart ./mychart --description "Make nginx say Foo" 

Release "mychart" has been upgraded. Happy Helming! 
NAME: mychart 
LAST DEPLOYED: Fri Dec 01 00:12:28 2022 
NAMESPACE: default 
STATUS: deployed 
REVISION: 2 
NOTES: 
1. Get the application URL by running these commands: 
  export NODE_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports[0].nodePort}" services helm-hw) 
  export NODE_IP=$(kubectl get nodes --namespace default -o jsonpath="{.items[0].status.addresses[0].address}") 
  echo http://$NODE_IP:$NODE_PORT 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pay attention to some useful flags which you can add to &lt;code&gt;upgrade&lt;/code&gt; command:&lt;br&gt;
&lt;code&gt;--description&lt;/code&gt; – add a short description for applied changes &lt;br&gt;
&lt;code&gt;--dry-run&lt;/code&gt; – simulate an upgrade, does no real changes in a cluster &lt;br&gt;
&lt;code&gt;--atomic&lt;/code&gt; – if set, upgrade process rolls back changes made in case of failed upgrade. The --wait flag will be set automatically if --atomic is used &lt;br&gt;
&lt;code&gt;--wait&lt;/code&gt; – if set and --wait enabled, will wait until all Jobs have been completed before marking the release as successful. It will wait for as long as --timeout &lt;br&gt;
&lt;code&gt;--cleanup-on-fail&lt;/code&gt; – allow deletion of new resources created in this upgrade when upgrade fails &lt;br&gt;
&lt;code&gt;--install&lt;/code&gt; – installs the charts if they are not already installed&lt;/p&gt;

&lt;p&gt;Now we can go and check our new message in the browser: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ow46jn6b6r4ojtpqk3c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ow46jn6b6r4ojtpqk3c.png" alt="Nginx Foo! custom message"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Do you remember that Helm saves old revisions in secrets? &lt;/p&gt;

&lt;p&gt;Let's check if is it true by selecting all the secrets filtered by chart name's label:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s kubectl get secrets -l name=mychart 

NAME                            TYPE                 DATA   AGE 
sh.helm.release.v1.mychart.v1   helm.sh/release.v1   1      16m 
sh.helm.release.v1.mychart.v2   helm.sh/release.v1   1      5m23s 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Describe the first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s kubectl describe secret sh.helm.release.v1.mychart.v1 

Name:         sh.helm.release.v1.mychart.v1 
Namespace:    default 
Labels:       modifiedAt=1669852845 
              name=mychart 
              owner=helm 
              status=superseded 
              version=1 
Annotations:  &amp;lt;none&amp;gt; 

Type:  helm.sh/release.v1 

Data 
==== 
release:  6476 bytes 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here you can find base64 encoded archive with chart files. &lt;/p&gt;

&lt;h2&gt;
  
  
  Do changes: &lt;code&gt;--set&lt;/code&gt; attribute, changes in ConfigMaps, rollout
&lt;/h2&gt;

&lt;p&gt;Another way to do some changes without file editing is using &lt;code&gt;--set&lt;/code&gt; flag. As far as we already have a parameter for nginx response we can update its value like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s helm3 upgrade mychart ./mychart --set nginx.message="Bar!" --description "Change nginx message to Bar!" 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unfortunately, nginx can not reload its configuration automatically, so if you check you don't see a new message right now despite the fact that manifest was successfully updated. &lt;/p&gt;

&lt;p&gt;To check the current state of the manifest use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s helm3 get manifest mychart 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are several ways how to reload an application and make it work with a new config: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;This &lt;a href="https://william-yeh.net/post/2019/06/autoreload-from-configmap/" rel="noopener noreferrer"&gt;article&lt;/a&gt; described good practices on how to set up auto-reloading using external signals or Reloader tool. Consider these for real cases. &lt;/li&gt;
&lt;li&gt;If you work in a sandbox and don't care about downtime you can just delete a pod with nginx, ReplicaSet will restore it and nginx will read a new config. &lt;/li&gt;
&lt;li&gt;Use kubectl for rollout.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I will stick to the last approach and redeploy the pod using the power of Kubernetes, do the same and after a new pod runs you will see "Bar!" message in your browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s kubectl rollout restart deployment/helm-hw 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F44vsr2arepz6k8nskusj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F44vsr2arepz6k8nskusj.png" alt="Nginx Bar! custom message"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But! &lt;/p&gt;

&lt;p&gt;You may wonder if there is any Helm-based approach to restarting pods after an upgrade.&lt;/p&gt;

&lt;p&gt;And the answer is yes! &lt;/p&gt;

&lt;p&gt;In Helm 2 was a flag &lt;code&gt;--recreate-pods&lt;/code&gt; but it was kind of imperative, it worked as deleting all the pods and led to downtime, so it was marked deprecated in Helm 3. &lt;/p&gt;

&lt;p&gt;Helm 3 came up with a &lt;a href="https://v3.helm.sh/docs/howto/charts_tips_and_tricks/#automatically-roll-deployments" rel="noopener noreferrer"&gt;solution based on checksums&lt;/a&gt; and it is recommended way to support auto-reloading. But it is not a topic of this guide, so I will use classical rollout. &lt;/p&gt;

&lt;h2&gt;
  
  
  Rollback changes: &lt;code&gt;history&lt;/code&gt;, &lt;code&gt;rollback&lt;/code&gt;, how to work with revisions
&lt;/h2&gt;

&lt;p&gt;To show chart's history use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s helm3 hist mychart 

REVISION UPDATED                 STATUS    CHART        APP VERSION DESCRIPTION                  
1       Wed Dec 01 00:00:59 2022 superseded mychart-0.1.0 1.0        Install complete             
2       Wed Dec 01 00:12:28 2022 superseded mychart-0.1.0 1.0        Make nginx say Foo           
3       Wed Dec 01 00:20:24 2022 deployed  mychart-0.1.0 1.0        Change nginx message to Bar! 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command &lt;code&gt;history&lt;/code&gt; (or &lt;code&gt;hist&lt;/code&gt; alias) displays a list of revisions for a specific chart, so you can see both of our updates here. You can find descriptions which were added in upgrade command usages are pretty useful, so don't hesitate to add a description when doing changes. &lt;/p&gt;

&lt;p&gt;If you use &lt;code&gt;hist&lt;/code&gt; command with &lt;code&gt;-h&lt;/code&gt; flag you will see in the annotation that the default maximum of revisions that will be included in history is &lt;code&gt;256&lt;/code&gt;, but does it mean that Helm keeps &lt;code&gt;256&lt;/code&gt; revisions archived? The answer is No! &lt;/p&gt;

&lt;p&gt;Let's check it with the next command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@helm-training# microk8s helm3 &lt;span class="nb"&gt;env&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;HELM_MAX_HISTORY 
&lt;span class="nv"&gt;HELM_MAX_HISTORY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"10"&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Helm has some properties configured in environment variables including max history property. If it is not set it would be &lt;code&gt;10&lt;/code&gt; – the value is hardcoded, if you set it to &lt;code&gt;0&lt;/code&gt; Helm would keep an &lt;code&gt;unlimited&lt;/code&gt; amount of revisions.&lt;/p&gt;

&lt;p&gt;Except for the description, you may be interested in other instruments to check particular revision, to make sure that it contains what you want, before doing a rollback. For that purpose Helm provides us the ability to check the chart's manifests for specific revision:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s helm3 get manifest mychart --revision 2 

... 
# Source: mychart/templates/configmap.yaml 
apiVersion: v1 
kind: ConfigMap 
metadata: 
  name: nginx-config 
data: 
  nginx.conf: ' 
events { 
} 
http { 
   server { 
       listen 80; 
       location / { 
         return 200 Foo!; 
       } 
   } 
} 
' 
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we can see that in 2nd revision nginx responded with "Foo" message. &lt;br&gt;
Another useful tip: use &lt;code&gt;NOTES.txt&lt;/code&gt; file to add release notes. Helm has a command to show notes for specific revision and the syntax is almost the same:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s helm3 get notes mychart --revision 2 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It does not show anything interesting right now because we didn't add notes, but feel free to use it for your purposes.&lt;/p&gt;

&lt;p&gt;And finally, when we are sure that we want to rollback to revision &lt;code&gt;2&lt;/code&gt; we should execute a command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s helm3 rollback mychart 2 

Rollback was a success! Happy Helming! 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;rollback&lt;/code&gt; command will restore a previous version of a chart from the specific secret and redeploy it to the cluster. Pay attention that &lt;code&gt;rollback&lt;/code&gt; command has &lt;code&gt;--recreate-pods&lt;/code&gt; optional flag to restart a pod imperatively in opposed to &lt;code&gt;upgrade&lt;/code&gt; command.&lt;br&gt;
Rollback also creates a new line in history with an automatically generated description:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s helm3 hist mychart 

REVISION UPDATED                 STATUS    CHART        APP VERSION DESCRIPTION                 
1       Wed Dec 01 00:00:59 2022 superseded mychart-0.1.0 1.0        Install complete             
2       Wed Dec 01 00:12:28 2022 superseded mychart-0.1.0 1.0        Make nginx say Foo           
3       Wed Dec 01 00:20:24 2022 superseded mychart-0.1.0 1.0        Change nginx message to Bar! 
4       Wed Dec 01 00:22:31 2022 deployed  mychart-0.1.0 1.0        Rollback to 2 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then if you roll back to revision &lt;code&gt;4&lt;/code&gt; it will be the same as if you roll back to revision &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prepare to distribution: &lt;code&gt;package&lt;/code&gt; command
&lt;/h2&gt;

&lt;p&gt;Helm has a special command to &lt;code&gt;package&lt;/code&gt; your chart in a tar archive. If you want to distribute your charts to consumers or work with third-party charts, this is the most popular format for a chart.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s helm3 package ./mychart 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Successfully packaged chart and saved it to: &lt;code&gt;/home/helm-training/mychart-0.1.0.tgz&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Helm uses &lt;code&gt;Chart.yaml&lt;/code&gt; file to build a package's name, so your path must contain it. By default, it will look like &lt;code&gt;name-version.tgz&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;To install chart from archive use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;microk8s helm3 install mychart mychart-0.1.0.tgz 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Repositories: local, external, registries
&lt;/h2&gt;

&lt;p&gt;If we talk about installing and downloading packages, we mean that there is some repository with charts. The most popular hub of repositories and charts for now is &lt;a href="https://artifacthub.io/packages/search" rel="noopener noreferrer"&gt;ArtifactHub&lt;/a&gt; (previously &lt;a href="https://helm.sh/blog/helm-hub-moving-to-artifact-hub/" rel="noopener noreferrer"&gt;HelmHub&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;It is good practice to work with charts like code: store them locally (or in a local repository) and update them carefully through a testing procedure. Operating with a remote repository only can lead to errors related to unwanted changes in new versions.&lt;/p&gt;

&lt;p&gt;If you're wondering how to create your local repository, in some obsolete guidelines you may find a keyword for &lt;code&gt;serve&lt;/code&gt;. It was a local chart repository installed on your machine for development purposes. But it was marked deprecated, and this is what the official documentation says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It didn't receive much uptake as a development tool and had numerous issues with its design. In the end, we decided to remove it and split it out as a plugin. &lt;br&gt;
For a similar experience to helm &lt;code&gt;serve&lt;/code&gt;, have a look at the local filesystem storage option in &lt;a href="https://chartmuseum.com/docs/#using-with-local-filesystem-storage" rel="noopener noreferrer"&gt;ChartMuseum&lt;/a&gt; and the &lt;a href="https://github.com/jdolitsky/helm-servecm" rel="noopener noreferrer"&gt;servecm plugin&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Helm documentation also provides &lt;a href="https://helm.sh/docs/topics/chart_repository/" rel="noopener noreferrer"&gt;guides&lt;/a&gt; on how to host your own repository based on existing solutions like JFrog, Google Cloud Storage, Cloudsmith, etc. &lt;/p&gt;

&lt;p&gt;If you don't want to use plugins for local repository you can run a http-server locally. &lt;/p&gt;

&lt;p&gt;A quick example based on dockerized nginx in four steps: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1.&lt;/strong&gt; Create a new folder, let's call it &lt;code&gt;/charts&lt;/code&gt;, and put packaged charts there.&lt;br&gt;
&lt;strong&gt;Step 2.&lt;/strong&gt; Init it as repo using. It will create &lt;code&gt;/charts/index.yaml&lt;/code&gt; - a file containing data about charts in the current directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;microk8s helm3 repo init 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3.&lt;/strong&gt; Run docker in a new terminal with nginx image with mounting &lt;code&gt;/charts&lt;/code&gt; to &lt;code&gt;/nginx/html&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -it -p 80:80 -v ~/helm-training/charts/:/usr/share/nginx/html nginx 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4.&lt;/strong&gt; Add localhost as external repo and work with it while the container is running.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;microk8s helm3 repo add local http://localhost 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is also good to mention that Amazon, Azure, Google and IBM supports OCI-based registry solutions to manage charts. A Helm repository is a way to house and distribute packaged Helm charts. An OCI-based registry can contain zero or more Helm repositories and each of those repositories can contain zero or more packaged Helm charts. &lt;a href="https://helm.sh/docs/topics/registries/" rel="noopener noreferrer"&gt;Click here&lt;/a&gt; to read more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install chart from an external repository
&lt;/h2&gt;

&lt;p&gt;Like an example of a 3rd party chart, let’s choose a &lt;code&gt;kube-ops-view&lt;/code&gt; &lt;a href="https://codeberg.org/hjacobs/kube-ops-view" rel="noopener noreferrer"&gt;tool&lt;/a&gt;. It is a simple monitoring tool for your Kubernetes cluster, it has a primitive UI that allows you to see pods installed, resource consumption for each node, etc. &lt;/p&gt;

&lt;p&gt;Let's search on &lt;a href="https://artifacthub.io/packages/helm/christianknell/kube-ops-view" rel="noopener noreferrer"&gt;ArtifactHub&lt;/a&gt; for &lt;code&gt;kube-ops-view&lt;/code&gt; tool, choose the repository and add it to our repo list. &lt;/p&gt;

&lt;p&gt;Or alternatively, we can use Helm's tool for a console search:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s helm3 search hub kube-ops-view 

URL                                               CHART VERSION APP VERSION DESCRIPTION                                        
https://artifacthub.io/packages/helm/geek-cookb... 1.2.2        20.4.0     A read-only system dashboard for multiple K8s c... 
https://artifacthub.io/packages/helm/christiank... 1.1.9        20.4.0     A Helm chart for bootstrapping kube-ops-view.      
https://artifacthub.io/packages/helm/fluent-ope... 0.1.2        20.4       A Helm chart kubeops, a read only kubernetes da... 

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the repo to our config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s helm3 repo add kube-ops-repo https://christianknell.github.io/helm-charts 

"kube-ops-repo" has been added to your repositories 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ask Helm to show a list of added repositories:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s helm3 repo list 

NAME         URL                                          
kube-ops-repo https://christianknell.github.io/helm-charts 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update the repo to get recent updates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s helm3 repo update 

Hang tight while we grab the latest from your chart repositories... 
...Successfully got an update from the "kube-ops-repo" chart repository 

Update Complete. ⎈Happy Helming!⎈ 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repo added, let's install &lt;code&gt;kube-ops-view&lt;/code&gt; chart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s helm3 install kube-ops-view kube-ops-repo/kube-ops-view 

NAME: kube-ops-view 
LAST DEPLOYED: Wed Dec 01 00:45:06 2022 
NAMESPACE: default 
STATUS: deployed 
REVISION: 1 
TEST SUITE: None 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the chart is installed it creates a service called &lt;code&gt;kube-ops-view&lt;/code&gt; inside your cluster. I will use port-forward command to access the service through a browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@helm-training# microk8s kubectl port-forward service/kube-ops-view 8801:80 

Forwarding from 127.0.0.1:8801 -&amp;gt; 8080 
Forwarding from [::1]:8801 -&amp;gt; 8080 
Handling connection for 8801 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's go to a browser to see the result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ferqwuc9b7l7uxev0jpai.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ferqwuc9b7l7uxev0jpai.png" alt="kube-ops-view UI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  EXTRA: What was not covered?
&lt;/h2&gt;

&lt;p&gt;Here I want to mention several good-to-know things that were not described in the article, but all of them are quite utilitarian and may be extremely helpful in real-life cases. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You can develop your own starter&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Remember the &lt;code&gt;create&lt;/code&gt; command? It generates some chart's structure. What if you need some specific structure with predefined templates, values, etc? &lt;/p&gt;

&lt;p&gt;Right! Helm &lt;a href="https://helm.sh/docs/topics/charts/#chart-starter-packs" rel="noopener noreferrer"&gt;supports&lt;/a&gt; custom &lt;code&gt;starters&lt;/code&gt; for this purpose. &lt;/p&gt;

&lt;p&gt;In a few words, you create a custom structure and use it with &lt;code&gt;--starter&lt;/code&gt; flag in addition to &lt;code&gt;create&lt;/code&gt; command.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You can configure dependencies between charts and create Library Charts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There are two chart types: &lt;code&gt;application&lt;/code&gt; and &lt;code&gt;library&lt;/code&gt;. Application is the default type and it is the standard chart which can be operated on fully. The &lt;a href="https://helm.sh/docs/topics/library_charts/" rel="noopener noreferrer"&gt;library chart&lt;/a&gt; provides utilities, functions, or another common configs for keeping charts DRY. Library charts can't be installed as application.&lt;/p&gt;

&lt;p&gt;We can add library or application charts one to another as a &lt;a href="https://helm.sh/docs/helm/helm_dependency/" rel="noopener noreferrer"&gt;dependency&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Dependencies can be configured in Chart.yaml like a list of chart in yaml notation:&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="c1"&gt;# Chart.yaml dependencies:  &lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;  
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.2.3"&lt;/span&gt;  
  &lt;span class="na"&gt;repository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://example.com/charts"&lt;/span&gt;  
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;memcached&lt;/span&gt;  
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.2.1"&lt;/span&gt;  
  &lt;span class="na"&gt;repository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://another.example.com/charts"&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Helm supports a batch of commands to work with dependencies: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;helm dependency build&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;&lt;code&gt;helm dependency list&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;helm dependency update&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;You can use hooks to run one pods before another&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://helm.sh/docs/topics/charts_hooks/" rel="noopener noreferrer"&gt;Hooks&lt;/a&gt; allow us to order starting manifests in a Kubernetes cluster.&lt;br&gt;
If you need to run one abstraction before another, do some &lt;code&gt;pre-install&lt;/code&gt;, &lt;code&gt;pre-delete&lt;/code&gt;, &lt;code&gt;post-upgrade&lt;/code&gt;, and &lt;a href="https://helm.sh/docs/topics/charts_hooks/#the-available-hooks" rel="noopener noreferrer"&gt;other&lt;/a&gt; steps, you can use hooks.&lt;br&gt;&lt;br&gt;
Think about it when you need to install a test DB before running a service, or clear a cache after an update.&lt;br&gt;&lt;br&gt;
Hooks are represented by classic Kubernetes abstractions like job or pod.&lt;br&gt;&lt;br&gt;
You are free to configure more than one hook to the same stage (for example, &lt;code&gt;post-install&lt;/code&gt;), in that case, hooks will be sorted by hook-weight (from lower to higher).&lt;br&gt;&lt;br&gt;
Hooks can also be configured for deletion after successful or failed execution according to &lt;a href="https://helm.sh/docs/topics/charts_hooks/#hook-deletion-policies" rel="noopener noreferrer"&gt;hook-deletion-policies&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://helm.sh/docs/" rel="noopener noreferrer"&gt;Helm docs&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/helm/helm" rel="noopener noreferrer"&gt;Helm GitHub&lt;/a&gt;&lt;br&gt;
&lt;a href="https://slurm.io/kubernetes-for-developers" rel="noopener noreferrer"&gt;Slurm.io: kubernetes for developers&lt;/a&gt;&lt;br&gt;
Blogs and articles: &lt;br&gt;
&lt;a href="https://docs.vmware.com/en/VMware-Application-Catalog/services/tutorials/GUID-create-first-helm-chart-index.html" rel="noopener noreferrer"&gt;link&lt;/a&gt;, &lt;a href="https://opensource.com/article/20/5/helm-charts" rel="noopener noreferrer"&gt;link&lt;/a&gt;, &lt;a href="https://polarsquad.com/blog/check-your-helm-deployments" rel="noopener noreferrer"&gt;link&lt;/a&gt;, &lt;a href="https://jhooq.com/helm-dry-run-install/" rel="noopener noreferrer"&gt;link&lt;/a&gt;, &lt;a href="https://rtfm.co.ua/en/helm-kubernetes-package-manager-an-overview-getting-started/" rel="noopener noreferrer"&gt;link&lt;/a&gt; &lt;br&gt;
and other mentioned in the text.&lt;/p&gt;

</description>
      <category>helm</category>
      <category>devjournal</category>
      <category>todayilearned</category>
    </item>
  </channel>
</rss>
