Skip to content

Quick example

peasoupio edited this page Dec 14, 2020 · 17 revisions

Quick example:

Let's build a quick and simple example even your boss will understand.
INV requires a good understanding of your existing ecosystem rules.
Our objective is to convert these rules into Groovy script files and let INV handle the rest for us.

First step: What are our ecosystem rules?

  • ServerA hosts AppA through Kubernetes
  • ServerB hosts AppB through IIS

With these rules, we can determine:

  • 6 INV's
  • At least 4 statements (or link or broadcasts/requirements)

Second step: How do we write it up in Groovy using INV?

Groovy is the most-suitable framework for understandable, idiomatic and quick-time to market solutions.
INV adds a layer using the DSL features of Groovy.
For more information about the syntax of INV, read this.

Hint:

Think of your INV's as individuals that are working together in a production chain.
Ask yourself "who does what" and "who tells what".

Bellow is a working prototype of our previous example statements:

serverA.groovy
inv {
    name "ServerA"

    broadcast { Server } using {
        id name: "server-a"
        ready {
            return [
                    host: "10.22.99.999",
                    install: { service ->
                        println "Installing service ${service} on 10.22.99.999"
                    }
            ]
        }
    }
}

A short-handed version would look like this:

inv {
    name "ServerA"

    broadcast { Server(name: "server-a" } using {
       ready {[
           host: "10.22.99.999",
           install: { service ->
               println "Installing service ${service} on 10.22.99.999"
           }
       ]}
   }
}

Also, here, the install method only prints a message to the console.
In a real life scenario, you would execute a real process using something similar to: "apt-get ${service}".execute().
Learn more about Groovy process facilitators here

kubernetes.groovy
inv {
    name "Kubernetes"

    require { Server } using {
        id name: "server-a"

        resolved {
            assert response

            response.install("kubectl")
        }
    }

    broadcast { Kubernetes } using {
        ready {
            return [
                    http      : "http://my-kubernetes.my.host.com",
                    port      : "8089", // not by default
                    installPod: { pod ->
                        println "Pod ${pod} has been installed"
                    }
            ]
        }
    }
}
appA.groovy
inv {
    name "appA"

    require { Kubernetes } using {
        resolved {
            response.installPod("my-mod-for-app-3")
        }
    }
}
serverB.groovy
inv {
    name "ServerB"

    broadcast { Server } using {
        id name: "server-b"
        ready {
            return [
                    host: "10.22.99.998",
            ]
        }
    }
}
iis.groovy
inv {
    name "iis"

    require { Server(name: "server-a") }

    broadcast { IIS } using {
        ready {
            return [
                    deploy: {webApp ->
                        println "IIS webapp ${webApp} has been deployed"
                    }
            ]
        }
    }
}
appB.groovy
inv {
    name "appB"

    require { IIS } using {
        resolved {
            response.deploy("my-web-app")
        }
    }
}
Important note:

INV enable a more encapsulated ecosystem.
In this simple case, we managed to implement a solution who protects critical information.
In our case, appB does not know which credentials or which host is used to deploy. Only IIS knows.

Default implementations:

INV comes with default implementations, such as "files (I/O), http, ..."
It serves mostly as concrete examples of how you could implement things, but you could use them in your ecosystem as well.
You can see them at here

Third step: How to run?

You may use the command-line utility (see at the top for available commands).
You may also use the Web Platform named INV Composer for a more elegant and accompanied path.

Here's our example above using the command-line utility:

git clone https://github.com/peasoupio/inv.git
inv run examples/githubHomepage/vars/*.groovy

IMPORTANT: Make sure mvn is installed and available

This command would generate the following output:

[INV] ---- [DIGEST] opened ----
[INV] [undefined] [../vars/appA.groovy] [appA]
[INV] [undefined] [../vars/appB.groovy] [appB]
[INV] [undefined] [../vars/iis.groovy] [iis]
[INV] [undefined] [../vars/kubernetes.groovy] [Kubernetes]
[INV] [undefined] [../vars/serverA.groovy] [ServerA]
[INV] [undefined] [../vars/serverB.groovy] [ServerB]
[INV] ---- [DIGEST] started ----
[INV] ---- [DIGEST] #1 (state=RUNNING) ----
[INV] [ServerB] => [BROADCAST] [Server] [name:server-b]
[INV] [ServerA] => [BROADCAST] [Server] [name:server-a]
[INV] ---- [DIGEST] #2 (state=RUNNING) ----
[INV] [iis] => [REQUIRE] [Server] [name:server-a]
[INV] [Kubernetes] => [REQUIRE] [Server] [name:server-a]
[INV] [iis] => [BROADCAST] [IIS] undefined
Installing service kubectl on 10.22.99.999
[INV] [Kubernetes] => [BROADCAST] [Kubernetes] undefined
[INV] ---- [DIGEST] #3 (state=RUNNING) ----
[INV] [appB] => [REQUIRE] [IIS] undefined
[INV] [appA] => [REQUIRE] [Kubernetes] undefined
Pod my-mod-for-app-3 has been installed
IIS webapp my-web-app has been deployed
[INV] [appA] => [BROADCAST] [App] [id:AppA]
[INV] [appB] => [BROADCAST] [App] [id:AppB]
[INV] ---- [DIGEST] completed ----
[INV] Completed INV(s): 6
[INV] Uncompleted INV(s): 0

By reading the output log, without having the fine details, we can determine:

  • Who needs what
  • Who says (or give) what
  • More precisely, in our case, which instance server (barebone or under Kubernetes) hosts AppA and etc.

REPO (p.e Git, SVN, etc.)

Yep, we thought about it.
INV is supporting every source code manager which is accessible programmatically. This includes cvs, svn, tfs, git, Mercurial, ...
For more information about the syntax of REPO, read this.

Here is an example using git to fetch our default implementation files:

myREPOFile.groovy
repo {
    name "inv"    
    url  "https://github.com/peasoupio/inv.git"

    hooks {
        init {
            "git clone ${url}" .
        }
        update {
            "git pull"
        }
    }
}

NOTE: INV toggle automatically from init to update upon path existence.

Graphs

INV output logs are human and "machine" readable.
In fact, you could also generate a graph from a previous execution.
NOTE: Output logs do not need to have exclusively INV messages, you may output messages to ease troubleshooting. INV only looks for lines starting with "[INV]".

So, continuing with our example, with our last execution (just above), using this command :

inv graph dot myprevious.log

Upon completion, we get a dot rendered output (see https://en.wikipedia.org/wiki/DOT_(graph_description_language))

strict digraph G {
  1 [ label="ServerA" ];
  2 [ label="[Server] [name:server-a]" ];
  3 [ label="ServerB" ];
  4 [ label="[Server] [name:server-b]" ];
  5 [ label="iis" ];
  6 [ label="[IIS] undefined" ];
  7 [ label="Kubernetes" ];
  8 [ label="[Kubernetes] undefined" ];
  9 [ label="appA" ];
  10 [ label="appB" ];
  11 [ label="files" ];
  12 [ label="[Files] undefined" ];
  13 [ label="maven" ];
  14 [ label="[Maven] undefined" ];
  15 [ label="my-app-1" ];
  16 [ label="my-app-2" ];
  17 [ label="[Artifact] com.mycompany.app:my-app-1" ];
  18 [ label="[Artifact] com.mycompany.app:my-app-2" ];
  2 -> 1;
  4 -> 3;
  5 -> 2;
  6 -> 5;
  7 -> 2;
  8 -> 7;
  9 -> 8;
  10 -> 6;
  12 -> 11;
  13 -> 12;
  14 -> 13;
  15 -> 14;
  16 -> 14;
  17 -> 15;
  18 -> 16;
  16 -> 17;
}

Using a visual DOT renderer, we get this image represention of the dot output :
Dotgraph image using WebGraphViz

Upon an error, what is available for troubleshooting?

Upon an error or more precisely when a statement is not matched, you'll see a report of the remaining INV's statements.
Here is an example:

[INV] ---- [DIGEST] started ----
[INV] ---- [DIGEST] #1 (state=RUNNING) ----
[INV] [my-server] => [BROADCAST] [Server] my-server-id
my-server-id has been broadcast
[INV] ---- [DIGEST] #2 (state=RUNNING) ----
[INV] [my-webservice] => [REQUIRE] [Server] my-server-id
[INV] [my-webservice] => [BROADCAST] [Endpoint] my-webservice-id
my-webservice-id has been broadcast
[INV] ---- [DIGEST] #3 (state=RUNNING) ----
[INV] nothing done
[INV] ---- [DIGEST] #4 (state=UNBLOATING) ----
[INV] nothing unbloated
[DEBUG] executor is shutting down
[INV] Completed INV(s): 2
[INV] Incompleted INV(s): 3
[WARN] INV(s): 
- my-app has 2 statement(s) left:
	1 requirement(s):
		[NOT MATCHED] [my-app] => [REQUIRE] [Endpoint] my-webservice-id-not-existing
	1 broadcast(s):
		[REQUIRED BY 2] [my-app] => [BROADCAST] [App] my-app-id
- my-app-2 has 4 statement(s) left:
	3 requirement(s):
		[NOT MATCHED] [my-app-2] => [REQUIRE] [Endpoint] my-webservice-id-not-existing
		[COULD MATCH] [my-app-2] => [REQUIRE] [App] my-app-id
		[UNBLOATABLE] [my-app-2] => [REQUIRE] [Endpoint] my-unbloatable-endpoint
	1 broadcast(s):
		[REQUIRED BY 1] [my-app-2] => [BROADCAST] [App] my-app-id-2
- my-app-3 has 5 statement(s) left:
	4 requirement(s):
		[NOT MATCHED] [my-app-3] => [REQUIRE] [Endpoint] my-webservice-id-not-existing
		[COULD MATCH] [my-app-3] => [REQUIRE] [App] my-app-id
		[COULD MATCH] [my-app-3] => [REQUIRE] [App] my-app-id-2
		[WOULD MATCH] [my-app-3] => [REQUIRE] [Endpoint] my-webservice-id
	1 broadcast(s):
		[REQUIRED BY 0] [my-app-3] => [BROADCAST] [App] my-app-id-3

[INV] ---- [DIGEST] completed ----
  • REQUIRED BY #: Shows how many require statements would have consumed this broadcast statement
  • NOT MATCHED: A require statement that CAN'T be matched with the remaining INV's as it is when reported.
  • WOULD MATCH: A require statement matching an available broadcast statement. The later was made available during a RUNNING or UNBLOATING cycle
  • COULD MATCH: A require statement matching a non-available broadcast statement. The later was found in the remaining INV's broadcast statements during this reporting.
  • UNBLOATABLE: A require statement that would have unbloatable

Doc and reporting

After each run, in the run folder (p.e ./runs/10/), a "reports" is created.
In the reports folder, INV writes a markdown file (.md) for every INV file read.
You can provide your own comments by using the markdown method.
Go