-
Notifications
You must be signed in to change notification settings - Fork 0
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.
- 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)
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.
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:
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
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"
}
]
}
}
}
inv {
name "appA"
require { Kubernetes } using {
resolved {
response.installPod("my-mod-for-app-3")
}
}
}
inv {
name "ServerB"
broadcast { Server } using {
id name: "server-b"
ready {
return [
host: "10.22.99.998",
]
}
}
}
inv {
name "iis"
require { Server(name: "server-a") }
broadcast { IIS } using {
ready {
return [
deploy: {webApp ->
println "IIS webapp ${webApp} has been deployed"
}
]
}
}
}
inv {
name "appB"
require { IIS } using {
resolved {
response.deploy("my-web-app")
}
}
}
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.
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
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.
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
:
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.
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 :
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
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