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.
Files changed (59) hide show
  1. package/.dockerignore +5 -0
  2. package/Dockerfile +34 -0
  3. package/LICENSE +373 -0
  4. package/README.md +485 -0
  5. package/TODO +34 -0
  6. package/conlink +11 -0
  7. package/conlink-start.sh +172 -0
  8. package/examples/dot.js +36 -0
  9. package/examples/index.html +11 -0
  10. package/examples/net2dot.yaml +21 -0
  11. package/examples/test1-compose.yaml +60 -0
  12. package/examples/test2-compose.yaml +31 -0
  13. package/examples/test2-network.yaml +5 -0
  14. package/examples/test3-network.yaml +5 -0
  15. package/examples/test4-multiple/all-compose.yaml +5 -0
  16. package/examples/test4-multiple/base-compose.yaml +25 -0
  17. package/examples/test4-multiple/node1-compose.yaml +17 -0
  18. package/examples/test4-multiple/nodes2-compose.yaml +20 -0
  19. package/examples/test4-multiple/web-network.yaml +2 -0
  20. package/examples/test5-geneve-compose.yaml +31 -0
  21. package/examples/test6-cfn.yaml +184 -0
  22. package/examples/test7-compose.yaml +31 -0
  23. package/examples/test8-compose.yaml +35 -0
  24. package/host-build.yaml +1 -0
  25. package/inspect.json +210 -0
  26. package/link-add.sh +197 -0
  27. package/link-del.sh +60 -0
  28. package/net2dot +11 -0
  29. package/notes.txt +82 -0
  30. package/old/Dockerfile.bak +26 -0
  31. package/old/add-link.sh +82 -0
  32. package/old/conlink +12 -0
  33. package/old/conlink.cljs +131 -0
  34. package/old/dot_gitignore +1 -0
  35. package/old/examples/test2-compose.yaml +32 -0
  36. package/old/examples/test2-network.yaml +42 -0
  37. package/old/move-link.sh +108 -0
  38. package/old/net2dot.py +122 -0
  39. package/old/notes-old.txt +97 -0
  40. package/old/package.json +16 -0
  41. package/old/schema.yaml +138 -0
  42. package/old/schema.yaml.bak +76 -0
  43. package/old/test2b-compose.yaml +18 -0
  44. package/old/veth-link.sh +96 -0
  45. package/package.json +15 -0
  46. package/schema-ish.yaml +29 -0
  47. package/schema.yaml +71 -0
  48. package/shadow-cljs.edn +33 -0
  49. package/src/conlink/addrs.cljc +63 -0
  50. package/src/conlink/core.cljs +772 -0
  51. package/src/conlink/net2dot.cljs +158 -0
  52. package/src/conlink/util.cljs +140 -0
  53. package/tests/invalid-schema-1.yaml +6 -0
  54. package/tests/invalid-schema-2.yaml +6 -0
  55. package/tests/invalid-schema-3.yaml +17 -0
  56. package/tests/invalid-schema-4.yaml +14 -0
  57. package/tests/invalid-schema-5.yaml +12 -0
  58. package/tests/invalid-schema-6.yaml +12 -0
  59. 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,6 @@
1
+ # should pass
2
+ links:
3
+ - {service: internet, type: dummy, dev: i0, ip: 8.8.8.8/24}
4
+
5
+ # should fail
6
+ commands: "abc"
@@ -0,0 +1,6 @@
1
+ # should pass
2
+ links:
3
+ - {service: internet, type: dummy, dev: i0, ip: 8.8.8.8/24}
4
+
5
+ # should fail
6
+ something-else: "abc"
@@ -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
+
@@ -0,0 +1,12 @@
1
+ # type and veth type requires bridge
2
+
3
+ links:
4
+ # should pass
5
+ - {service: node, bridge: s1}
6
+ - {container: node, bridge: s1}
7
+
8
+ # Should fail
9
+ - {type: veth, bridge: s1}
10
+ - {bridge: s1}
11
+
12
+
@@ -0,0 +1,12 @@
1
+
2
+ links:
3
+ # should pass
4
+ - {service: node, bridge: s1}
5
+
6
+ tunnels:
7
+ # should pass
8
+ - {type: geneve, bridge: s1, remote: 1.1.1.1, vni: 1001}
9
+
10
+ # Should error
11
+ - {type: geneve, remote: 1.1.1.1, vni: 1001}
12
+
@@ -0,0 +1 @@
1
+ blah