conlink 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +5 -0
- package/Dockerfile +34 -0
- package/LICENSE +373 -0
- package/README.md +485 -0
- package/TODO +34 -0
- package/conlink +11 -0
- package/conlink-start.sh +172 -0
- package/examples/dot.js +36 -0
- package/examples/index.html +11 -0
- package/examples/net2dot.yaml +21 -0
- package/examples/test1-compose.yaml +60 -0
- package/examples/test2-compose.yaml +31 -0
- package/examples/test2-network.yaml +5 -0
- package/examples/test3-network.yaml +5 -0
- package/examples/test4-multiple/all-compose.yaml +5 -0
- package/examples/test4-multiple/base-compose.yaml +25 -0
- package/examples/test4-multiple/node1-compose.yaml +17 -0
- package/examples/test4-multiple/nodes2-compose.yaml +20 -0
- package/examples/test4-multiple/web-network.yaml +2 -0
- package/examples/test5-geneve-compose.yaml +31 -0
- package/examples/test6-cfn.yaml +184 -0
- package/examples/test7-compose.yaml +31 -0
- package/examples/test8-compose.yaml +35 -0
- package/host-build.yaml +1 -0
- package/inspect.json +210 -0
- package/link-add.sh +197 -0
- package/link-del.sh +60 -0
- package/net2dot +11 -0
- package/notes.txt +82 -0
- package/old/Dockerfile.bak +26 -0
- package/old/add-link.sh +82 -0
- package/old/conlink +12 -0
- package/old/conlink.cljs +131 -0
- package/old/dot_gitignore +1 -0
- package/old/examples/test2-compose.yaml +32 -0
- package/old/examples/test2-network.yaml +42 -0
- package/old/move-link.sh +108 -0
- package/old/net2dot.py +122 -0
- package/old/notes-old.txt +97 -0
- package/old/package.json +16 -0
- package/old/schema.yaml +138 -0
- package/old/schema.yaml.bak +76 -0
- package/old/test2b-compose.yaml +18 -0
- package/old/veth-link.sh +96 -0
- package/package.json +15 -0
- package/schema-ish.yaml +29 -0
- package/schema.yaml +71 -0
- package/shadow-cljs.edn +33 -0
- package/src/conlink/addrs.cljc +63 -0
- package/src/conlink/core.cljs +772 -0
- package/src/conlink/net2dot.cljs +158 -0
- package/src/conlink/util.cljs +140 -0
- package/tests/invalid-schema-1.yaml +6 -0
- package/tests/invalid-schema-2.yaml +6 -0
- package/tests/invalid-schema-3.yaml +17 -0
- package/tests/invalid-schema-4.yaml +14 -0
- package/tests/invalid-schema-5.yaml +12 -0
- package/tests/invalid-schema-6.yaml +12 -0
- package/tmp/conlink/.env +1 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/usr/bin/env nbb
|
|
2
|
+
|
|
3
|
+
(ns conlink.net2dot
|
|
4
|
+
(:require [clojure.string :as S]
|
|
5
|
+
[clojure.pprint :refer [pprint]]
|
|
6
|
+
[promesa.core :as P]
|
|
7
|
+
[conlink.util :refer [parse-opts Eprintln Epprint fatal]]
|
|
8
|
+
[conlink.core :as conlink]))
|
|
9
|
+
|
|
10
|
+
(def usage "
|
|
11
|
+
net2dot: convert conlink network config to GraphViz/dot representation.
|
|
12
|
+
|
|
13
|
+
Usage:
|
|
14
|
+
net2dot [options]
|
|
15
|
+
|
|
16
|
+
Options:
|
|
17
|
+
-v, --verbose Show verbose output (stderr)
|
|
18
|
+
[env: VERBOSE]
|
|
19
|
+
--network-file NETWORK-FILE... Network config file
|
|
20
|
+
--compose-file COMPOSE-FILE... Docker compose file with network config
|
|
21
|
+
")
|
|
22
|
+
|
|
23
|
+
(def DEFAULT-PROPS "shape=box fontsize=12 style=filled penwidth=1")
|
|
24
|
+
(def CONLINK-PROPS "style=\"rounded,filled\" fillcolor = \"#c1b5c7\" color = \"#9673a6\"")
|
|
25
|
+
(def BRIDGE-PROPS "style= filled fillcolor=\"#dae8fc\" color=\"#6c8ebf\"")
|
|
26
|
+
(def HOST-PROPS "style=filled fillcolor=\"#f5f5f5\" color=\"#666666\"")
|
|
27
|
+
(def TUNNEL-PROPS "fillcolor=\"#a5a5a5\" color=\"#888888\"")
|
|
28
|
+
(def CONTAINER-PROPS "style=\"rounded,filled\" fillcolor = \"#e1d5e7\" color = \"#9673a6\"")
|
|
29
|
+
(def SERVICE-PROPS (str CONTAINER-PROPS " fillcolor = \"#d1c5e7\" penwidth = 2"))
|
|
30
|
+
(def INTF-PROPS "width=0.1 height=0.1 fontsize=10 fillcolor=\"#ffbb9e\" color=\"#d7db00\"")
|
|
31
|
+
(def NIC-PROPS "fontsize=12 fillcolor=\"#ffbb9e\" color=\"#d7db00\"")
|
|
32
|
+
|
|
33
|
+
(set! conlink/INTF-MAX-LEN 100)
|
|
34
|
+
|
|
35
|
+
(defn dot-id [n]
|
|
36
|
+
(-> n
|
|
37
|
+
(S/replace #"[:]" "_COLON_")
|
|
38
|
+
(S/replace #"[-]" "_DASH_")
|
|
39
|
+
(S/replace #"[*]" "_STAR_")
|
|
40
|
+
(S/replace #"[$]" "_DOLLAR_")
|
|
41
|
+
(S/replace #"[{]" "_LCURLY_")
|
|
42
|
+
(S/replace #"[}]" "_LCURLY_")
|
|
43
|
+
(S/replace #"[ ]" "_SPACE_")))
|
|
44
|
+
|
|
45
|
+
(defn digraph [links tunnels]
|
|
46
|
+
(let [veth-links (filter #(= :veth (:type %)) links)
|
|
47
|
+
vlan-links (filter #(conlink/VLAN-TYPES (:type %)) links)]
|
|
48
|
+
(S/join
|
|
49
|
+
"\n"
|
|
50
|
+
(flatten
|
|
51
|
+
[(str "digraph D {")
|
|
52
|
+
(str " splines = true;")
|
|
53
|
+
(str " compound = true;")
|
|
54
|
+
(str " node [" DEFAULT-PROPS "];")
|
|
55
|
+
|
|
56
|
+
""
|
|
57
|
+
" // host system"
|
|
58
|
+
(str " subgraph cluster_host {")
|
|
59
|
+
(str " label = \"host system\";")
|
|
60
|
+
(str " " HOST-PROPS ";")
|
|
61
|
+
|
|
62
|
+
""
|
|
63
|
+
" // main link nodes"
|
|
64
|
+
(for [{:keys [dev dev-id]} links]
|
|
65
|
+
(str " " (dot-id dev-id) " [label=\"" dev "\" " INTF-PROPS "];"))
|
|
66
|
+
|
|
67
|
+
""
|
|
68
|
+
" // containers and their links/interfaces"
|
|
69
|
+
(for [[container-name links] (group-by (comp :name :container) links)]
|
|
70
|
+
[(str " subgraph cluster_" (dot-id container-name) " {")
|
|
71
|
+
(str " label = \"" (:container-label (first links)) "\";")
|
|
72
|
+
(if (:service (first links))
|
|
73
|
+
(str " " SERVICE-PROPS ";")
|
|
74
|
+
(str " " CONTAINER-PROPS ";"))
|
|
75
|
+
(for [link links]
|
|
76
|
+
(str " " (dot-id (:dev-id link))))
|
|
77
|
+
(str " }")])
|
|
78
|
+
|
|
79
|
+
""
|
|
80
|
+
" // bridges, tunnels, veth connections"
|
|
81
|
+
(str " subgraph cluster_conlink {")
|
|
82
|
+
(str " label = \"conlink/network\";")
|
|
83
|
+
(str " " CONLINK-PROPS ";")
|
|
84
|
+
(for [bridge (set (keep :bridge veth-links))
|
|
85
|
+
:let [blinks (filter #(= bridge (:bridge %)) veth-links)]]
|
|
86
|
+
[(str " subgraph cluster_bridge_" (dot-id bridge) " {")
|
|
87
|
+
(str " label = \"" bridge "\";")
|
|
88
|
+
(str " " BRIDGE-PROPS ";")
|
|
89
|
+
(str " bridge_" (dot-id bridge) " [shape=point style=invis];")
|
|
90
|
+
(for [{:keys [dev-id outer-dev]} blinks]
|
|
91
|
+
[(str " " (dot-id outer-dev)
|
|
92
|
+
" [label=\"" outer-dev "\" " INTF-PROPS "];")
|
|
93
|
+
(str " " (dot-id dev-id) " -> " (dot-id outer-dev))])
|
|
94
|
+
(for [{:keys [bridge outer-dev]} tunnels]
|
|
95
|
+
(str " " (dot-id outer-dev)
|
|
96
|
+
" [label=\"" outer-dev "\" " INTF-PROPS "];"))
|
|
97
|
+
(str " }")])
|
|
98
|
+
(str " }")
|
|
99
|
+
|
|
100
|
+
""
|
|
101
|
+
" // vlan/vtap links"
|
|
102
|
+
(for [outer-dev (set (keep :outer-dev vlan-links))
|
|
103
|
+
:let [olinks (filter #(= outer-dev (:outer-dev %)) vlan-links)]]
|
|
104
|
+
[(str " " (dot-id outer-dev) " [label=\"" outer-dev "\" " NIC-PROPS "];")
|
|
105
|
+
(for [{:keys [dev-id outer-dev type vlanid vni ip]} olinks
|
|
106
|
+
:let [label (str (name type) (when vlanid
|
|
107
|
+
(str " " vlanid)))]]
|
|
108
|
+
(str " " (dot-id dev-id) " -> " (dot-id outer-dev)
|
|
109
|
+
" [label=\"" label "\"];"))])
|
|
110
|
+
|
|
111
|
+
" // end of host system"
|
|
112
|
+
(str " }")
|
|
113
|
+
|
|
114
|
+
""
|
|
115
|
+
" // remote hosts and tunnels links"
|
|
116
|
+
(for [{:keys [outer-dev remote]} tunnels]
|
|
117
|
+
[(str " " (dot-id remote)
|
|
118
|
+
" [label=\"remote host '" remote "'\" " TUNNEL-PROPS "];")
|
|
119
|
+
(str " " (dot-id outer-dev) " -> " (dot-id remote)) ])
|
|
120
|
+
|
|
121
|
+
"}\n"]))))
|
|
122
|
+
|
|
123
|
+
(defn enrich-link [{:as link :keys [service container]}]
|
|
124
|
+
(let [name (if service
|
|
125
|
+
(str "S_" service) #_(str "*_" service "_*")
|
|
126
|
+
container)
|
|
127
|
+
clabel (if service
|
|
128
|
+
(str "service '"service "'")
|
|
129
|
+
(str "container '" container "'"))
|
|
130
|
+
container {:id "CID"
|
|
131
|
+
:pid 3
|
|
132
|
+
:index 1
|
|
133
|
+
:name name}]
|
|
134
|
+
(merge
|
|
135
|
+
(conlink/link-instance-enrich link container 2)
|
|
136
|
+
{:container-label clabel})))
|
|
137
|
+
|
|
138
|
+
(defn enrich-tunnel [tunnel]
|
|
139
|
+
(conlink/tunnel-instance-enrich tunnel 2))
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
(defn main
|
|
143
|
+
[& args]
|
|
144
|
+
(P/let
|
|
145
|
+
[{:keys [verbose compose-file network-file]} (parse-opts usage args)
|
|
146
|
+
_ (when (and (empty? network-file) (empty? compose-file))
|
|
147
|
+
(fatal 2 "either --network-file or --compose-file is required"))
|
|
148
|
+
network-config (P/-> (conlink/load-configs compose-file network-file)
|
|
149
|
+
(conlink/enrich-network-config))
|
|
150
|
+
links (map enrich-link (:links network-config))
|
|
151
|
+
tunnels (map enrich-tunnel (:tunnels network-config))
|
|
152
|
+
dot-graph (digraph links tunnels)]
|
|
153
|
+
(when verbose
|
|
154
|
+
(Eprintln "Links:")
|
|
155
|
+
(Epprint links)
|
|
156
|
+
(Eprintln "Tunnels:")
|
|
157
|
+
(Epprint tunnels))
|
|
158
|
+
(println dot-graph)))
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
(ns conlink.util
|
|
2
|
+
(:require [cljs.pprint :refer [pprint]]
|
|
3
|
+
[clojure.string :as S]
|
|
4
|
+
[clojure.walk :refer [postwalk]]
|
|
5
|
+
[clojure.edn :as edn]
|
|
6
|
+
[promesa.core :as P]
|
|
7
|
+
[cljs-bean.core :refer [->clj]]
|
|
8
|
+
["util" :refer [promisify]]
|
|
9
|
+
["fs" :as fs]
|
|
10
|
+
["child_process" :as cp]
|
|
11
|
+
["neodoc" :as neodoc]))
|
|
12
|
+
|
|
13
|
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
14
|
+
;; Argument processing
|
|
15
|
+
(defn clean-opts [arg-map]
|
|
16
|
+
(reduce (fn [o [a v]]
|
|
17
|
+
(let [k (keyword (S/replace a #"^[-<]*([^>]*)[>]*$" "$1"))]
|
|
18
|
+
(assoc o k (or (get o k) v))))
|
|
19
|
+
{} arg-map))
|
|
20
|
+
|
|
21
|
+
(defn parse-opts [usage argv & [opts]]
|
|
22
|
+
(-> usage
|
|
23
|
+
(neodoc/run (clj->js (merge {:optionsFirst true
|
|
24
|
+
:smartOptions true
|
|
25
|
+
:argv (or argv [])}
|
|
26
|
+
opts)))
|
|
27
|
+
js->clj
|
|
28
|
+
clean-opts))
|
|
29
|
+
|
|
30
|
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
31
|
+
;; General functions
|
|
32
|
+
|
|
33
|
+
(def Eprn #(binding [*print-fn* *print-err-fn*] (apply prn %&)))
|
|
34
|
+
(def Eprintln #(binding [*print-fn* *print-err-fn*] (apply println %&)))
|
|
35
|
+
(def Epprint #(binding [*print-fn* *print-err-fn*] (pprint %)))
|
|
36
|
+
|
|
37
|
+
(defn fatal [code & args]
|
|
38
|
+
(when (seq args)
|
|
39
|
+
(apply Eprintln args))
|
|
40
|
+
(js/process.exit code))
|
|
41
|
+
|
|
42
|
+
(defn deep-merge [a b]
|
|
43
|
+
(merge-with #(cond (map? %1) (recur %1 %2)
|
|
44
|
+
(vector? %1) (vec (concat %1 %2))
|
|
45
|
+
(sequential? %1) (concat %1 %2)
|
|
46
|
+
:else %2)
|
|
47
|
+
a b))
|
|
48
|
+
|
|
49
|
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
50
|
+
;; String functions
|
|
51
|
+
|
|
52
|
+
(defn snake->pascal [v]
|
|
53
|
+
(S/join
|
|
54
|
+
"" (for [[chr1 & chrN] (-> v .toLowerCase (S/split #"-"))]
|
|
55
|
+
(apply str (.toUpperCase chr1) chrN))))
|
|
56
|
+
|
|
57
|
+
(defn pascal->snake [v]
|
|
58
|
+
(.toLowerCase
|
|
59
|
+
(S/join "-" (re-seq #"[^a-z]+[a-z]*[^A-Z]*" v))))
|
|
60
|
+
|
|
61
|
+
(defn trim [s] (S/replace s #"\s*$" ""))
|
|
62
|
+
|
|
63
|
+
(defn right-pad [s pad]
|
|
64
|
+
(.padEnd (str s) pad " "))
|
|
65
|
+
|
|
66
|
+
(defn left-pad [s pad]
|
|
67
|
+
(.padStart (str s) pad " "))
|
|
68
|
+
|
|
69
|
+
(defn indent [s pre]
|
|
70
|
+
(-> s
|
|
71
|
+
(S/replace #"[\n]*$" "")
|
|
72
|
+
(S/replace #"(^|[\n])" (str "$1" pre))))
|
|
73
|
+
|
|
74
|
+
(def INTERPOLATE-RE (js/RegExp. "[$](?:([$])|([_a-z][_a-z0-9]*)|{([_a-z][_a-z0-9]*)(?:(:?[-?])([^}]*))?}|())" "gi"))
|
|
75
|
+
|
|
76
|
+
(defn interpolate [s env]
|
|
77
|
+
(.replaceAll
|
|
78
|
+
s INTERPOLATE-RE
|
|
79
|
+
(fn [_ escaped named braced sep value invalid offset groups]
|
|
80
|
+
(cond escaped "$"
|
|
81
|
+
named (get env named "")
|
|
82
|
+
sep (let [unset? (not (contains? env braced))
|
|
83
|
+
unset-or-null? (empty? (get env braced nil))]
|
|
84
|
+
(condp = sep
|
|
85
|
+
":-" (if unset-or-null? value (get env braced))
|
|
86
|
+
"-" (if unset? value (get env braced))
|
|
87
|
+
":?" (when unset-or-null? (throw (js/Error value)))
|
|
88
|
+
"?" (when unset? (throw (js/Error value)))))
|
|
89
|
+
braced (get env braced "")
|
|
90
|
+
invalid (str "$" invalid)))))
|
|
91
|
+
|
|
92
|
+
(defn interpolate-walk [o env]
|
|
93
|
+
(postwalk #(if (string? %) (interpolate % env) %) o))
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
97
|
+
;; Promise-based exec and file functions
|
|
98
|
+
|
|
99
|
+
(def exec-promise (promisify cp/exec))
|
|
100
|
+
(defn exec [cmd & [opts]]
|
|
101
|
+
(P/let [opts (merge {:encoding "utf8" :stdio "pipe"} opts)
|
|
102
|
+
res (exec-promise cmd (clj->js opts))]
|
|
103
|
+
(->clj res)))
|
|
104
|
+
(defn spawn [cmd & [opts]]
|
|
105
|
+
(P/create
|
|
106
|
+
(fn [resolve reject]
|
|
107
|
+
(let [opts (merge {:stdio "pipe" :shell true} opts)
|
|
108
|
+
res (atom {:stdout [] :stderr []})
|
|
109
|
+
res-fn (fn [code]
|
|
110
|
+
{:code code
|
|
111
|
+
:stdout (S/join "" (:stdout @res))
|
|
112
|
+
:stderr (S/join "" (:stderr @res))})
|
|
113
|
+
child (doto (cp/spawn cmd (clj->js opts))
|
|
114
|
+
(.on "close" (fn [code]
|
|
115
|
+
(if (= 0 code)
|
|
116
|
+
(resolve (res-fn code))
|
|
117
|
+
(reject (res-fn code))))))]
|
|
118
|
+
(when-let [stdout (.-stdout child)]
|
|
119
|
+
(.setEncoding stdout "utf8")
|
|
120
|
+
(.on stdout "data" #(swap! res update :stdout conj %)))
|
|
121
|
+
(when-let [stderr (.-stderr child)]
|
|
122
|
+
(.setEncoding stderr "utf8")
|
|
123
|
+
(.on stderr "data" #(swap! res update :stderr conj %)))))))
|
|
124
|
+
|
|
125
|
+
(def read-file (promisify fs/readFile))
|
|
126
|
+
(def write-file (promisify fs/writeFile))
|
|
127
|
+
|
|
128
|
+
(defn load-config [file]
|
|
129
|
+
(P/let [raw (P/-> file read-file .toString)
|
|
130
|
+
cfg (cond
|
|
131
|
+
(re-seq #".*\.(yml|yaml)$" file)
|
|
132
|
+
(.parse (js/require "yaml") raw)
|
|
133
|
+
|
|
134
|
+
(re-seq #".*\.json$" file)
|
|
135
|
+
(js/JSON.parse raw)
|
|
136
|
+
|
|
137
|
+
(re-seq #".*\.edn$" file)
|
|
138
|
+
(edn/read-string raw))]
|
|
139
|
+
(->clj cfg)))
|
|
140
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# type and veth type requires bridge
|
|
2
|
+
|
|
3
|
+
links:
|
|
4
|
+
# should pass
|
|
5
|
+
- {service: node, type: dummy}
|
|
6
|
+
- {service: node, type: veth, bridge: s1}
|
|
7
|
+
- {service: node, bridge: s1}
|
|
8
|
+
- {service: node, type: vlan, vlanid: 100}
|
|
9
|
+
|
|
10
|
+
# Should error
|
|
11
|
+
- {service: node, type: magic}
|
|
12
|
+
- {service: node, type: veth}
|
|
13
|
+
- {service: node}
|
|
14
|
+
- {service: node, type: vlan}
|
|
15
|
+
- {service: node, bridge: s1, vlanid: 100}
|
|
16
|
+
|
|
17
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
links:
|
|
2
|
+
# should pass
|
|
3
|
+
- {service: node, bridge: s1, ip: 10.0.1.1/16}
|
|
4
|
+
- {service: node, bridge: s1, mac: "00:11:99:00:00:99"}
|
|
5
|
+
- {service: node, bridge: s1, mac: "00:11:99:0a:0b:ff"}
|
|
6
|
+
|
|
7
|
+
# Should error
|
|
8
|
+
- {service: node, bridge: s1, ip: 10.0.1}
|
|
9
|
+
- {service: node, bridge: s1, ip: 10.0.1.1}
|
|
10
|
+
- {service: node, bridge: s1, ip: 1011.0.1.1/16}
|
|
11
|
+
- {service: node, bridge: s1, mac: "00:11:99:0a:0b"}
|
|
12
|
+
- {service: node, bridge: s1, mac: "00:11:99:0a:0b:fg"}
|
|
13
|
+
|
|
14
|
+
|
package/tmp/conlink/.env
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
blah
|