Skip to content

Quick example

peasoupio edited this page Jan 18, 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 rendering of our previous example statements:

serverA.groovy
inv {
    name "ServerA"

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

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

    require inv.Server using {
        id name: "server-a"

        resolved {
            assert response

            response.install("kubectl")
        }
    }

    broadcast inv.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 inv.Kubernetes using {
        resolved {
            response.installPod("my-mod-for-app-3")
        }
    }
}
iis.groovy
inv {
    name "iis"

    require inv.Server(name: "server-a")

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

    require inv.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 ÌIS knows.

Default implementations:

INV comes with default implementations, such as "files (I/O), http, ..."
It serves mostly as concrete examples on 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 using the command-line utility:

inv load ./example/githubHomepage/*.groov

This command would generate the following output:

[INV] file: ./example/githubHomepage/appA.groovy
[INV] file: ./example/githubHomepage/appB.groovy
[INV] file: ./example/githubHomepage/iis.groovy
[INV] file: ./example/githubHomepage/kubernetes.groovy
[INV] file: ./example/githubHomepage/serverA.groovy
[INV] file: ./example/githubHomepage/serverB.groovy
[INV] ---- [DIGEST] started ----
[INV] ---- [DIGEST] #1 (state=RUNNING) ----
[INV] [ServerA] => [BROADCAST] [Server] [name:server-a]
[INV] [ServerB] => [BROADCAST] [Server] [name:server-b]
[INV] ---- [DIGEST] #2 (state=RUNNING) ----
[INV] [iis] => [REQUIRE] [Server] [name:server-a]
[INV] [iis] => [BROADCAST] [IIS] undefined
[INV] [Kubernetes] => [REQUIRE] [Server] [name:server-a]
[INV] [Kubernetes] => [BROADCAST] [Kubernetes] undefined
Installing service kubectl on 10.22.99.999
[INV] ---- [DIGEST] #3 (state=RUNNING) ----
[INV] [appA] => [REQUIRE] [Kubernetes] undefined
[INV] [appB] => [REQUIRE] [IIS] undefined
Pod my-mod-for-app-3 has been installed
IIS webapp my-web-app has been deployed
[INV] ---- [DIGEST] completed ----

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.

SCM

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

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

mySCMFile.groovy
scm {
    name "default-files"
    path "choose your path..."
    scm  "https://github.com/peasoupio/inv.git"
    entry "defaults/files/inv.groovy"
    hooks {
        init {
            "git clone ${scm}" .
        }
        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 does 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 out 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?

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