conlink 2.0.0 → 2.0.2
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/Dockerfile +2 -1
- package/README.md +18 -14
- package/examples/net2dot.yaml +2 -2
- package/examples/test4-multiple/modes/all/deps +1 -0
- package/examples/test4-multiple/{base-compose.yaml → modes/base/compose.yaml} +3 -3
- package/examples/test4-multiple/{node1-compose.yaml → modes/node1/compose.yaml} +0 -3
- package/examples/test4-multiple/modes/node1/deps +1 -0
- package/examples/test4-multiple/{nodes2-compose.yaml → modes/nodes2/compose.yaml} +0 -5
- package/examples/test4-multiple/modes/nodes2/deps +1 -0
- package/examples/test4-multiple/modes/web/compose.yaml +5 -0
- package/examples/test4-multiple/modes/web/deps +1 -0
- package/examples/test6-cfn.yaml +1 -2
- package/mdc +108 -0
- package/net2dot +1 -1
- package/net2dot.cljs +113 -0
- package/package.json +6 -1
- package/scripts/copy.sh +48 -0
- package/scripts/wait.sh +73 -0
- package/shadow-cljs.edn +1 -1
- package/src/conlink/addrs.cljc +3 -0
- package/src/conlink/core.cljs +21 -7
- package/src/conlink/util.cljs +13 -2
- package/TODO +0 -34
- package/examples/test4-multiple/all-compose.yaml +0 -5
- package/examples/test4-multiple/web-network.yaml +0 -2
- package/host-build.yaml +0 -1
- package/inspect.json +0 -210
- package/notes.txt +0 -82
- package/old/Dockerfile.bak +0 -26
- package/old/add-link.sh +0 -82
- package/old/conlink +0 -12
- package/old/conlink.cljs +0 -131
- package/old/dot_gitignore +0 -1
- package/old/examples/test2-compose.yaml +0 -32
- package/old/examples/test2-network.yaml +0 -42
- package/old/move-link.sh +0 -108
- package/old/net2dot.py +0 -122
- package/old/notes-old.txt +0 -97
- package/old/package.json +0 -16
- package/old/schema.yaml +0 -138
- package/old/schema.yaml.bak +0 -76
- package/old/test2b-compose.yaml +0 -18
- package/old/veth-link.sh +0 -96
- package/schema-ish.yaml +0 -29
- package/src/conlink/net2dot.cljs +0 -158
- package/tests/invalid-schema-1.yaml +0 -6
- package/tests/invalid-schema-2.yaml +0 -6
- package/tests/invalid-schema-3.yaml +0 -17
- package/tests/invalid-schema-4.yaml +0 -14
- package/tests/invalid-schema-5.yaml +0 -12
- package/tests/invalid-schema-6.yaml +0 -12
- package/tmp/conlink/.env +0 -1
package/Dockerfile
CHANGED
|
@@ -28,7 +28,8 @@ RUN apt-get -y install libpcap-dev tcpdump iproute2 iputils-ping curl \
|
|
|
28
28
|
openvswitch-switch openvswitch-testcontroller
|
|
29
29
|
|
|
30
30
|
COPY --from=build /app/ /app/
|
|
31
|
-
ADD link-add.sh link-del.sh
|
|
31
|
+
ADD link-add.sh link-del.sh /app/
|
|
32
|
+
ADD schema.yaml /app/build/
|
|
32
33
|
|
|
33
34
|
ENV PATH /app:$PATH
|
|
34
35
|
WORKDIR /app
|
package/README.md
CHANGED
|
@@ -258,8 +258,7 @@ container (`node1`) and switch (`s1`) that is connected to the router
|
|
|
258
258
|
defined in the first compose file.
|
|
259
259
|
|
|
260
260
|
```
|
|
261
|
-
|
|
262
|
-
docker-compose up --build --force-recreate
|
|
261
|
+
MODES_DIR=./examples/test4-multiple/modes ./mdc node1 up --build --force-recreate
|
|
263
262
|
```
|
|
264
263
|
|
|
265
264
|
Ping the router host from `node`:
|
|
@@ -273,8 +272,7 @@ two node2 replicas and a switch (`s2`) that is connected to the
|
|
|
273
272
|
router.
|
|
274
273
|
|
|
275
274
|
```
|
|
276
|
-
|
|
277
|
-
docker-compose up --build --force-recreate
|
|
275
|
+
MODES_DIR=./examples/test4-multiple/modes ./mdc node1,nodes2 up --build --force-recreate
|
|
278
276
|
```
|
|
279
277
|
|
|
280
278
|
From both `node2` replicas, ping `node1` across the switches and `r0` router:
|
|
@@ -289,8 +287,7 @@ conlink using an addition network file `web-network.yaml`. The network
|
|
|
289
287
|
file starts up a simple web server on the router.
|
|
290
288
|
|
|
291
289
|
```
|
|
292
|
-
|
|
293
|
-
docker-compose up --build --force-recreate
|
|
290
|
+
MODES_DIR=./examples/test4-multiple/modes ./mdc node1,nodes2,web up --build --force-recreate
|
|
294
291
|
```
|
|
295
292
|
|
|
296
293
|
From the second `node2`, perform a download from the web server running on the
|
|
@@ -300,6 +297,14 @@ router host:
|
|
|
300
297
|
docker-compose exec --index 2 node2 wget -O- 10.0.0.100
|
|
301
298
|
```
|
|
302
299
|
|
|
300
|
+
We can simplify the above launch by using the `all` mode, which contains
|
|
301
|
+
depends on `node1`, `nodes2`, and `web`. Each of those modes depends on
|
|
302
|
+
`base`, so there's no need to specify that again (transitive deps).
|
|
303
|
+
|
|
304
|
+
```
|
|
305
|
+
MODES_DIR=./examples/test4-multiple/modes ./mdc all up --build --force-recreate
|
|
306
|
+
```
|
|
307
|
+
|
|
303
308
|
Remove the `.env` file as a final cleanup step:
|
|
304
309
|
|
|
305
310
|
```
|
|
@@ -453,16 +458,15 @@ python3 -m http.server 8080
|
|
|
453
458
|
```
|
|
454
459
|
|
|
455
460
|
Use the `net2dot` script to transform a network
|
|
456
|
-
configuration into a GraphViz data file (dot language).
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
the following in another window:
|
|
461
|
+
configuration into a GraphViz data file (dot language). To render the
|
|
462
|
+
network configuration for example test1, run the following in another
|
|
463
|
+
window:
|
|
460
464
|
|
|
461
465
|
```
|
|
462
|
-
./
|
|
466
|
+
./conlink --show-config --compose-file examples/test1-compose.yaml | ./net2dot > examples/test1.dot
|
|
463
467
|
```
|
|
464
468
|
|
|
465
|
-
Then load `http://localhost:8080` in your browser to see the rendered
|
|
469
|
+
Then load `http://localhost:8080?data=test1.dot` in your browser to see the rendered
|
|
466
470
|
image.
|
|
467
471
|
|
|
468
472
|
The file `examples/net2dot.yaml` contains a configuration that
|
|
@@ -470,10 +474,10 @@ combines many different configuration elements (veth links, dummy
|
|
|
470
474
|
interfaces, vlan type links, tunnels, etc).
|
|
471
475
|
|
|
472
476
|
```
|
|
473
|
-
./
|
|
477
|
+
./conlink --network-file examples/net2dot.yaml --show-config | ./net2dot > examples/net2dot.dot
|
|
474
478
|
```
|
|
475
479
|
|
|
476
|
-
Then load `http://localhost:8080` in your browser.
|
|
480
|
+
Then load `http://localhost:8080?data=net2dot.dot` in your browser.
|
|
477
481
|
|
|
478
482
|
|
|
479
483
|
## Copyright & License
|
package/examples/net2dot.yaml
CHANGED
|
@@ -15,7 +15,7 @@ links:
|
|
|
15
15
|
- {type: macvlan, service: nodeH, dev: mv0, outer-dev: eni1, mode: bridge}
|
|
16
16
|
|
|
17
17
|
tunnels:
|
|
18
|
-
- {type: geneve, bridge: s1, vni: 1001, remote: "
|
|
19
|
-
- {type: geneve, bridge: s3, vni: 1002, remote: "
|
|
18
|
+
- {type: geneve, bridge: s1, vni: 1001, remote: "10.0.0.1"}
|
|
19
|
+
- {type: geneve, bridge: s3, vni: 1002, remote: "10.0.0.2"}
|
|
20
20
|
|
|
21
21
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
node1 nodes2 web
|
|
@@ -10,7 +10,7 @@ services:
|
|
|
10
10
|
- {bridge: s0, dev: e0, ip: "10.0.0.100/24"}
|
|
11
11
|
|
|
12
12
|
network:
|
|
13
|
-
build: {context:
|
|
13
|
+
build: {context: .}
|
|
14
14
|
image: conlink
|
|
15
15
|
pid: host
|
|
16
16
|
network_mode: none
|
|
@@ -19,7 +19,7 @@ services:
|
|
|
19
19
|
volumes:
|
|
20
20
|
- /var/run/docker.sock:/var/run/docker.sock
|
|
21
21
|
- /var/lib/docker:/var/lib/docker
|
|
22
|
-
-
|
|
22
|
+
- ./:/test
|
|
23
23
|
working_dir: /test
|
|
24
|
-
command: /app/build/conlink.js --compose-file ${COMPOSE_FILE
|
|
24
|
+
command: /app/build/conlink.js --compose-file ${COMPOSE_FILE:?COMPOSE_FILE must be set}
|
|
25
25
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
base
|
|
@@ -4,11 +4,7 @@ x-network:
|
|
|
4
4
|
links:
|
|
5
5
|
- {service: r0, bridge: s2, dev: e2, ip: "10.2.0.100/24"}
|
|
6
6
|
|
|
7
|
-
|
|
8
7
|
services:
|
|
9
|
-
network:
|
|
10
|
-
command: /app/build/conlink.js --compose-file ${COMPOSE_FILE:-examples/test4-multiple/nodes2-compose.yaml}
|
|
11
|
-
|
|
12
8
|
node2:
|
|
13
9
|
image: alpine
|
|
14
10
|
scale: 2
|
|
@@ -17,4 +13,3 @@ services:
|
|
|
17
13
|
x-network:
|
|
18
14
|
links:
|
|
19
15
|
- {bridge: s2, ip: "10.2.0.1/16", route: "default via 10.2.0.100"}
|
|
20
|
-
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
base
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
base
|
package/examples/test6-cfn.yaml
CHANGED
|
@@ -88,10 +88,9 @@ Resources:
|
|
|
88
88
|
# echo "podman ps" | sudo -i -u ubuntu bash
|
|
89
89
|
|
|
90
90
|
## Download conlink image and repo
|
|
91
|
-
docker pull lonocloud/conlink
|
|
91
|
+
docker pull lonocloud/conlink
|
|
92
92
|
git clone https://github.com/LonoCloud/conlink /root/conlink
|
|
93
93
|
cd /root/conlink
|
|
94
|
-
git checkout -b config-and-cljs-refactor origin/config-and-cljs-refactor
|
|
95
94
|
|
|
96
95
|
#cfn-signal -e 0 --stack ${AWS::StackName} --region ${AWS::Region} --resource WaitHandle
|
|
97
96
|
cat > /tmp/signal << EOF
|
package/mdc
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
set -e
|
|
4
|
+
shopt -s dotglob # recursive copy of dot files too
|
|
5
|
+
|
|
6
|
+
die() { echo >&2 "${*}"; exit 1; }
|
|
7
|
+
vecho() { [ "${VERBOSE}" ] && echo >&2 "${*}" || true; }
|
|
8
|
+
realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"; }
|
|
9
|
+
|
|
10
|
+
[ "${*}" ] || die "Usage: ${0} MODES [DOCKER-COMPOSE-ARGS]"
|
|
11
|
+
|
|
12
|
+
VERBOSE="${VERBOSE:-}"
|
|
13
|
+
MODES_DIR="${MODES_DIR:-./modes}"
|
|
14
|
+
ENV_FILE="${ENV_FILE:-.env}"
|
|
15
|
+
MDC_FILES_DIR="${MDC_FILES_DIR:-./.files}"
|
|
16
|
+
LS=$(which ls)
|
|
17
|
+
RESOLVE_DEPS="${RESOLVE_DEPS-./node_modules/@lonocloud/resolve-deps/resolve-deps.py}"
|
|
18
|
+
DOCKER_COMPOSE="${DOCKER_COMPOSE:-docker-compose}"
|
|
19
|
+
|
|
20
|
+
MODE_SPEC="${1}"; shift
|
|
21
|
+
if [ "${RESOLVE_DEPS}" ]; then
|
|
22
|
+
MODES="$(${RESOLVE_DEPS} "${MODES_DIR}" ${MODE_SPEC})"
|
|
23
|
+
else
|
|
24
|
+
MODES="${MODE_SPEC//,/ }"
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
echo >&2 "MODES: ${MODES}"
|
|
28
|
+
vecho "ENV_FILE: ${ENV_FILE}"
|
|
29
|
+
|
|
30
|
+
declare -A FINISHED
|
|
31
|
+
COMPOSE_FILE=./.compose-empty.yaml
|
|
32
|
+
COMPOSE_PROFILES=
|
|
33
|
+
MDC_MODE_DIRS=
|
|
34
|
+
cat /dev/null > ${ENV_FILE}-mdc-tmp
|
|
35
|
+
echo -e "version: '2.4'\nservices: {}" > ./.compose-empty.yaml
|
|
36
|
+
|
|
37
|
+
vecho "Removing ${MDC_FILES_DIR}"
|
|
38
|
+
case "$(basename ${MDC_FILES_DIR})" in
|
|
39
|
+
.|/) die "MDC_FILES_DIR must not be '.' or '/'";;
|
|
40
|
+
esac
|
|
41
|
+
[ -d "${MDC_FILES_DIR}" ] && rm -r${VERBOSE:+v} ${MDC_FILES_DIR}/
|
|
42
|
+
mkdir -p "${MDC_FILES_DIR}"
|
|
43
|
+
|
|
44
|
+
for mode in ${MODES}; do
|
|
45
|
+
# Only process each mode once
|
|
46
|
+
[ "${FINISHED[${mode}]}" ] && continue
|
|
47
|
+
FINISHED["${mode}"]=1
|
|
48
|
+
|
|
49
|
+
# mode dir must exist
|
|
50
|
+
[ -d "${MODES_DIR}/${mode}" ] || \
|
|
51
|
+
die "No mode dir found for ${mode}"
|
|
52
|
+
|
|
53
|
+
MDC_MODE_DIRS="${MDC_MODE_DIRS},${MODES_DIR}/${mode}"
|
|
54
|
+
|
|
55
|
+
# mode can refer to a compose file in multiple ways
|
|
56
|
+
cfiles="${MODES_DIR}/${mode}/compose.yaml ${MODES_DIR}/${mode}/compose.yml ${MODES_DIR}/${mode}/docker-compose.yaml ${MODES_DIR}/${mode}/docker-compose.yml"
|
|
57
|
+
for cfile in ${cfiles}; do
|
|
58
|
+
if [ -e "${cfile}" ]; then
|
|
59
|
+
COMPOSE_FILE="${COMPOSE_FILE}:${cfile}"
|
|
60
|
+
break
|
|
61
|
+
fi
|
|
62
|
+
done
|
|
63
|
+
|
|
64
|
+
# Add MODE_ prefixed compose profile for each mode
|
|
65
|
+
COMPOSE_PROFILES="${COMPOSE_PROFILES},MODE_${mode}"
|
|
66
|
+
|
|
67
|
+
# if there is a mode specific env file then include it
|
|
68
|
+
efiles="${MODES_DIR}/${mode}/env ${MODES_DIR}/${mode}/.env"
|
|
69
|
+
for efile in ${efiles}; do
|
|
70
|
+
if [ -e "${efile}" ]; then
|
|
71
|
+
echo "### mdc begin mode ${mode}" >> ${ENV_FILE}-mdc-tmp
|
|
72
|
+
vecho "cat ${efile} >> ${ENV_FILE}-mdc-tmp"
|
|
73
|
+
cat ${efile} >> ${ENV_FILE}-mdc-tmp
|
|
74
|
+
echo "### mdc end mode ${mode}" >> ${ENV_FILE}-mdc-tmp
|
|
75
|
+
fi
|
|
76
|
+
done
|
|
77
|
+
|
|
78
|
+
# if there are mode specific files then copy them to MDC_FILES_DIR
|
|
79
|
+
if [ -d "${MODES_DIR}/${mode}" ]; then
|
|
80
|
+
for vfd in $(cd ${MODES_DIR}/${mode} && $LS -d */files 2>/dev/null || true); do
|
|
81
|
+
dest=${MDC_FILES_DIR}/${vfd%/files}
|
|
82
|
+
mkdir -p ${dest}
|
|
83
|
+
vecho cp -a ${MODES_DIR}/${mode}/${vfd}/* ${dest}
|
|
84
|
+
cp -a ${MODES_DIR}/${mode}/${vfd}/* ${dest}
|
|
85
|
+
done
|
|
86
|
+
fi
|
|
87
|
+
done
|
|
88
|
+
|
|
89
|
+
COMPOSE_FILE="${COMPOSE_FILE#:}"
|
|
90
|
+
vecho "COMPOSE_FILE: ${COMPOSE_FILE}"
|
|
91
|
+
echo "COMPOSE_FILE=${COMPOSE_FILE}" >> ${ENV_FILE}-mdc-tmp
|
|
92
|
+
echo "COMPOSE_DIR=$(realpath $(dirname ${ENV_FILE}))" >> ${ENV_FILE}-mdc-tmp
|
|
93
|
+
|
|
94
|
+
COMPOSE_PROFILES="${COMPOSE_PROFILES#,}"
|
|
95
|
+
vecho "COMPOSE_PROFILES: ${COMPOSE_PROFILES}"
|
|
96
|
+
echo "COMPOSE_PROFILES=${COMPOSE_PROFILES}" >> ${ENV_FILE}-mdc-tmp
|
|
97
|
+
|
|
98
|
+
MDC_MODE_DIRS="${MDC_MODE_DIRS#,}"
|
|
99
|
+
vecho "MDC_MODE_DIRS: ${MDC_MODE_DIRS}"
|
|
100
|
+
echo "MDC_MODE_DIRS=\"${MDC_MODE_DIRS}\"" >> ${ENV_FILE}-mdc-tmp
|
|
101
|
+
|
|
102
|
+
vecho mv ${ENV_FILE}-mdc-tmp ${ENV_FILE}
|
|
103
|
+
mv ${ENV_FILE}-mdc-tmp ${ENV_FILE}
|
|
104
|
+
|
|
105
|
+
if [ "${*}" ]; then
|
|
106
|
+
vecho "Running: ${DOCKER_COMPOSE} --env-file "${ENV_FILE}" ${@}"
|
|
107
|
+
exec ${DOCKER_COMPOSE} --env-file "${ENV_FILE}" "${@}"
|
|
108
|
+
fi
|
package/net2dot
CHANGED
package/net2dot.cljs
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env nbb
|
|
2
|
+
|
|
3
|
+
;; Copyright (c) 2023, Viasat, Inc
|
|
4
|
+
;; Licensed under MPL 2.0
|
|
5
|
+
|
|
6
|
+
(ns net2dot
|
|
7
|
+
(:require [clojure.string :as S]
|
|
8
|
+
[clojure.pprint :refer [pprint]]
|
|
9
|
+
[cljs-bean.core :refer [->clj ->js]]
|
|
10
|
+
["fs" :as fs]
|
|
11
|
+
["ts-graphviz" :refer [Digraph Subgraph toDot Node Edge]]))
|
|
12
|
+
|
|
13
|
+
;; Reads JSON network config from stdin and outputs to dot (GraphViz)
|
|
14
|
+
|
|
15
|
+
(def DEFAULT-PROPS {:shape "box" :fontsize 14 :style "filled" :penwidth 1})
|
|
16
|
+
(def CONLINK-PROPS {:fillcolor "#c5d1e7" :color "#7396a0" :style "rounded,filled" :penwidth 3})
|
|
17
|
+
(def CON-PROPS {:fillcolor "#dfe8f1" :color "#7396a6" :style "rounded,filled"})
|
|
18
|
+
(def SVC-PROPS {:fillcolor "#c1d0d7" :color "#7396a6" :style "rounded,filled" :penwidth 2})
|
|
19
|
+
(def BRIDGE-PROPS {:fillcolor "#c8badc" :color "#6e509f"})
|
|
20
|
+
(def HOST-PROPS {:fillcolor "#f5f5f5" :color "#666666"})
|
|
21
|
+
(def INTF-PROPS {:fillcolor "#e8e8c8" :color "#af6b4e" :fontsize 10 :width 0.1 :height 0.1})
|
|
22
|
+
(def NIC-PROPS {:fillcolor "#e8e8c8" :color "#af6b4e" :fontsize 14})
|
|
23
|
+
(def TUN-PROPS {:fillcolor "#a5f5a5" :color "#888888"})
|
|
24
|
+
|
|
25
|
+
(defn dot-id [n]
|
|
26
|
+
(-> (name n)
|
|
27
|
+
(S/replace #"[:]" "_COLON_")
|
|
28
|
+
(S/replace #"[-]" "_DASH_")
|
|
29
|
+
(S/replace #"[*]" "_STAR_")
|
|
30
|
+
(S/replace #"[$]" "_DOLLAR_")
|
|
31
|
+
(S/replace #"[{]" "_LCURLY_")
|
|
32
|
+
(S/replace #"[}]" "_LCURLY_")
|
|
33
|
+
(S/replace #"[ ]" "_SPACE_")))
|
|
34
|
+
|
|
35
|
+
(defn node-props [label props]
|
|
36
|
+
(merge DEFAULT-PROPS props {:label label}))
|
|
37
|
+
|
|
38
|
+
(defn digraph [props]
|
|
39
|
+
(Digraph. (->js {:splines true :compound true})))
|
|
40
|
+
|
|
41
|
+
(defn subgraph [parent id label props]
|
|
42
|
+
(let [n (Subgraph. id (->js (node-props label props)))]
|
|
43
|
+
^obj (.addSubgraph parent n)
|
|
44
|
+
n))
|
|
45
|
+
|
|
46
|
+
(defn node [parent id label props]
|
|
47
|
+
(let [n (Node. id (->js (node-props label props)))]
|
|
48
|
+
^obj (.addNode parent n)
|
|
49
|
+
n))
|
|
50
|
+
|
|
51
|
+
(defn edge [parent idA idB label props]
|
|
52
|
+
(let [n (Edge. #js [idA idB] (->js (node-props label props)))]
|
|
53
|
+
^obj (.addEdge parent n)
|
|
54
|
+
n))
|
|
55
|
+
|
|
56
|
+
(defn render [network-config]
|
|
57
|
+
(let [graph (digraph {:splines true :compound true})
|
|
58
|
+
host (subgraph graph "cluster_host" "host system" HOST-PROPS)
|
|
59
|
+
conlink (subgraph host "cluster_conlink" "conlink/network" CONLINK-PROPS)
|
|
60
|
+
bridges (reduce
|
|
61
|
+
#(->> (subgraph conlink (str "cluster_bridge_" %2)
|
|
62
|
+
%2 BRIDGE-PROPS)
|
|
63
|
+
(assoc %1 %2))
|
|
64
|
+
{} (keep :bridge (:links network-config)))
|
|
65
|
+
services (reduce
|
|
66
|
+
#(->> (subgraph host (str "cluster_service_" (dot-id %2))
|
|
67
|
+
(str "service '" (name %2) "'") SVC-PROPS)
|
|
68
|
+
(assoc %1 %2))
|
|
69
|
+
{} (keys (:services network-config)))
|
|
70
|
+
containers (reduce
|
|
71
|
+
#(->> (subgraph host (str "cluster_container_" (dot-id %2))
|
|
72
|
+
(str "container '" (name %2) "'") CON-PROPS)
|
|
73
|
+
(assoc %1 %2))
|
|
74
|
+
{} (keys (:containers network-config)))]
|
|
75
|
+
|
|
76
|
+
(doseq [link (:links network-config)]
|
|
77
|
+
(let [{:keys [service container dev outer-dev bridge base]} link
|
|
78
|
+
cname (or service container)
|
|
79
|
+
cnode (get (if service services containers) (keyword cname))
|
|
80
|
+
dev-id (str cname ":" (name dev))
|
|
81
|
+
in-intf (node cnode (dot-id dev-id) dev INTF-PROPS)]
|
|
82
|
+
(when (#{:conlink :host} (keyword base))
|
|
83
|
+
(let [outer-dev (or outer-dev
|
|
84
|
+
(str (if service (str service "_1") container)
|
|
85
|
+
"-" (name dev)))
|
|
86
|
+
out-id (str "out-" outer-dev)
|
|
87
|
+
out-parent (condp = (keyword base)
|
|
88
|
+
:conlink (get bridges bridge)
|
|
89
|
+
:host host)
|
|
90
|
+
{:keys [type vlanid]} link
|
|
91
|
+
[elabel iprops] (if (= "host" base)
|
|
92
|
+
[(str (name type) (when vlanid
|
|
93
|
+
(str " " vlanid)))
|
|
94
|
+
NIC-PROPS]
|
|
95
|
+
["" INTF-PROPS])
|
|
96
|
+
out-intf (node out-parent (dot-id out-id) outer-dev iprops)
|
|
97
|
+
edge (edge graph in-intf out-intf elabel {})]
|
|
98
|
+
true))))
|
|
99
|
+
|
|
100
|
+
(doseq [{:keys [type remote bridge vni]} (:tunnels network-config)]
|
|
101
|
+
(let [br (get bridges bridge)
|
|
102
|
+
rt (node graph (dot-id remote)
|
|
103
|
+
(str "remote host " remote) TUN-PROPS)
|
|
104
|
+
intf (node br (dot-id (str type "-" vni))
|
|
105
|
+
(str type "-" vni) INTF-PROPS)]
|
|
106
|
+
(edge graph intf rt "" {})))
|
|
107
|
+
|
|
108
|
+
(toDot graph)))
|
|
109
|
+
|
|
110
|
+
(let [stdin (fs/readFileSync "/dev/stdin" "utf8")
|
|
111
|
+
network-config (->clj (js/JSON.parse stdin))
|
|
112
|
+
dot-graph (render network-config)]
|
|
113
|
+
(println dot-graph))
|
package/package.json
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "conlink",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
|
+
"description": "conlink - Declarative Low-Level Networking for Containers",
|
|
5
|
+
"repository": "https://github.com/LonoCloud/conlink",
|
|
6
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
4
7
|
"dependencies": {
|
|
8
|
+
"@lonocloud/resolve-deps": "^0.0.2",
|
|
5
9
|
"ajv": "^8.12.0",
|
|
6
10
|
"dockerode": "^3.3.4",
|
|
7
11
|
"nbb": "^1.2.179",
|
|
8
12
|
"neodoc": "^2.0.2",
|
|
13
|
+
"ts-graphviz": "^1.8.1",
|
|
9
14
|
"yaml": "^2.2.1"
|
|
10
15
|
},
|
|
11
16
|
"devDependencies": {
|
package/scripts/copy.sh
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2023, Viasat, Inc
|
|
4
|
+
# Licensed under MPL 2.0
|
|
5
|
+
|
|
6
|
+
# Add files from SRC_DIR to DST_DIR with string interpolation.
|
|
7
|
+
# Any '{{FOO}}' tokens are replaced with value of corresponding
|
|
8
|
+
# environment variable FOO (but only if defined).
|
|
9
|
+
|
|
10
|
+
die() { local ret=${1}; shift; echo >&2 "${*}"; exit $ret; }
|
|
11
|
+
|
|
12
|
+
case "${1}" in -T|--template) TEMPLATE=1; shift ;; esac
|
|
13
|
+
|
|
14
|
+
src_dir="${1}"; shift || die 2 "Usage: ${0} [-T|--template] SRC_DIR DST_DIR"
|
|
15
|
+
dst_dir="${1}"; shift || die 2 "Usage: ${0} [-T|--template] SRC_DIR DST_DIR"
|
|
16
|
+
[ "${1}" = "--" ] && shift
|
|
17
|
+
|
|
18
|
+
[ -d "${src_dir}" ] || die 2 "Not a directory: '${src_dir}'"
|
|
19
|
+
[ -d "${dst_dir}" ] || die 2 "Not a directory: '${dst_dir}'"
|
|
20
|
+
|
|
21
|
+
(cd "${src_dir}" && find . -type f) | while read src_file; do
|
|
22
|
+
src="${src_dir}/${src_file}"
|
|
23
|
+
dst="${dst_dir}/${src_file}"
|
|
24
|
+
mkdir -p $(dirname "${dst}") || die 1 "Failed to make target directory"
|
|
25
|
+
echo cp -a "${src}" "${dst}"
|
|
26
|
+
cp -a "${src}" "${dst}" || die 1 "Failed to copy file"
|
|
27
|
+
# TODO: make this configurable
|
|
28
|
+
chown root.root "${dst}" || die 1 "Unable to set ownership"
|
|
29
|
+
|
|
30
|
+
[ -z "${TEMPLATE}" ] && continue
|
|
31
|
+
|
|
32
|
+
# match all {{FOO}} style variables and replace from environment
|
|
33
|
+
for v in $(cat "${dst}" | grep -o '{{[^ }{]*}}' | sed 's/[}{]//g' | sort -u); do
|
|
34
|
+
if set | grep -qs "^${v}="; then
|
|
35
|
+
val=$(set | grep "^${v}=" | cut -f 2 -d '=' \
|
|
36
|
+
| sed "s/^['\"]\(.*\)['\"]$/\1/" \
|
|
37
|
+
| sed 's/[\/&]/\\&/g')
|
|
38
|
+
echo "Replacing '{{${v}}}' with '${val}' in '${dst}'"
|
|
39
|
+
sed -i "s/{{${v}}}/${val}/g" "${dst}"
|
|
40
|
+
fi
|
|
41
|
+
done
|
|
42
|
+
done
|
|
43
|
+
|
|
44
|
+
if [ "${*}" ]; then
|
|
45
|
+
exec "${@}"
|
|
46
|
+
else
|
|
47
|
+
true
|
|
48
|
+
fi
|
package/scripts/wait.sh
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2023, Viasat, Inc
|
|
4
|
+
# Licensed under MPL 2.0
|
|
5
|
+
|
|
6
|
+
die() { local ret=${1}; shift; echo >&2 "${*}"; exit $ret; }
|
|
7
|
+
|
|
8
|
+
NC=$(command -v nc 2>/dev/null)
|
|
9
|
+
SOCAT=$(command -v socat 2>/dev/null)
|
|
10
|
+
BASH=$(command -v bash 2>/dev/null)
|
|
11
|
+
WAIT_SLEEP=${WAIT_SLEEP:-1}
|
|
12
|
+
|
|
13
|
+
do_sleep() {
|
|
14
|
+
echo "Failed: '${typ} ${arg}'. Sleep ${WAIT_SLEEP} seconds before retry"
|
|
15
|
+
sleep ${WAIT_SLEEP}
|
|
16
|
+
}
|
|
17
|
+
check_tcp() {
|
|
18
|
+
if [ "${NC}" ]; then ${NC} -z -w 1 ${1} ${2} > /dev/null
|
|
19
|
+
elif [ "${SOCAT}" ]; then ${SOCAT} /dev/null TCP:${1}:${2},connect-timeout=2
|
|
20
|
+
elif [ "${BASH}" ]; then timeout 1 ${BASH} -c "echo > /dev/tcp/${1}/${2}"
|
|
21
|
+
else die 1 "Could not find nc, socat, or bash"
|
|
22
|
+
fi
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
while [ "${*}" ]; do
|
|
26
|
+
typ="${1}"; shift
|
|
27
|
+
arg="${1}"
|
|
28
|
+
[ "${arg}" = "--" ] && die 2 "No arg found for type '${typ}'"
|
|
29
|
+
case "${typ}" in
|
|
30
|
+
--) break; ;;
|
|
31
|
+
-f|--file)
|
|
32
|
+
while [ ! -e "${arg}" ]; do do_sleep; done
|
|
33
|
+
echo "File '${arg}' exists"
|
|
34
|
+
;;
|
|
35
|
+
-i|--if|--intf)
|
|
36
|
+
while [ ! -e /sys/class/net/${arg}/ifindex ]; do do_sleep; done
|
|
37
|
+
echo "Interface '${arg}' exists"
|
|
38
|
+
;;
|
|
39
|
+
-I|--ip)
|
|
40
|
+
while ! grep -qs "^${arg}\>" /proc/net/route; do do_sleep; done
|
|
41
|
+
echo "Interface '${arg}' has IP/routing"
|
|
42
|
+
;;
|
|
43
|
+
-t|--tcp)
|
|
44
|
+
host=${arg%:*}
|
|
45
|
+
port=${arg##*:}
|
|
46
|
+
[ "${host}" -a "${port}" ] || die 2 "Illegal host/port '${arg}'"
|
|
47
|
+
while ! check_tcp ${host} ${port}; do do_sleep; done
|
|
48
|
+
echo "TCP listener is reachable at '${arg}' "
|
|
49
|
+
;;
|
|
50
|
+
-u|--umask)
|
|
51
|
+
umask ${arg}
|
|
52
|
+
echo "Set umask to ${arg}"
|
|
53
|
+
;;
|
|
54
|
+
-c|--cmd|--command)
|
|
55
|
+
while ! ${arg}; do do_sleep; done
|
|
56
|
+
echo "Command successful: ${arg}"
|
|
57
|
+
;;
|
|
58
|
+
-s|--sleep)
|
|
59
|
+
WAIT_SLEEP=${arg}
|
|
60
|
+
echo "Changed WAIT_SLEEP from ${WAIT_SLEEP} to ${arg}"
|
|
61
|
+
;;
|
|
62
|
+
*)
|
|
63
|
+
echo "Unknown option: ${typ}"
|
|
64
|
+
exit 1
|
|
65
|
+
;;
|
|
66
|
+
esac
|
|
67
|
+
shift
|
|
68
|
+
done
|
|
69
|
+
|
|
70
|
+
if [ "${*}" ]; then
|
|
71
|
+
echo "Running: ${*}"
|
|
72
|
+
exec ${*}
|
|
73
|
+
fi
|
package/shadow-cljs.edn
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
:net2dot
|
|
22
22
|
{:target :node-script
|
|
23
23
|
:main conlink.net2dot/main
|
|
24
|
-
:output-dir "build/"
|
|
24
|
+
:output-dir "build/net2dot"
|
|
25
25
|
:output-to "build/net2dot.js"
|
|
26
26
|
;; Don't try and connect back to shadow-cljs process
|
|
27
27
|
:devtools {:enabled false :console-support false}
|
package/src/conlink/addrs.cljc
CHANGED
package/src/conlink/core.cljs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
;; Copyright (c) 2023, Viasat, Inc
|
|
2
|
+
;; Licensed under MPL 2.0
|
|
2
3
|
|
|
3
4
|
(ns conlink.core
|
|
4
5
|
(:require [clojure.string :as S]
|
|
@@ -7,7 +8,7 @@
|
|
|
7
8
|
[cljs-bean.core :refer [->clj ->js]]
|
|
8
9
|
[conlink.util :refer [parse-opts Eprintln fatal
|
|
9
10
|
trim indent interpolate-walk deep-merge
|
|
10
|
-
spawn read-file load-config]]
|
|
11
|
+
spawn read-file load-config resolve-path]]
|
|
11
12
|
[conlink.addrs :as addrs]
|
|
12
13
|
#_["ajv$default" :as Ajv]
|
|
13
14
|
#_["dockerode$default" :as Docker]))
|
|
@@ -25,6 +26,7 @@ Usage:
|
|
|
25
26
|
General Options:
|
|
26
27
|
-v, --verbose Show verbose output (stderr)
|
|
27
28
|
[env: VERBOSE]
|
|
29
|
+
--show-config Print loaded network config JSON and exit
|
|
28
30
|
--bridge-mode BRIDGE-MODE Bridge mode (ovs or linux) to use for
|
|
29
31
|
bridge/switch connections
|
|
30
32
|
[default: ovs]
|
|
@@ -45,6 +47,10 @@ General Options:
|
|
|
45
47
|
;; TODO: :service should require either command line option or
|
|
46
48
|
;; detection of running in a compose project (but not both).
|
|
47
49
|
|
|
50
|
+
;; TODO: shadow-cljs doesn't support *file*
|
|
51
|
+
;;(path/dirname *file*))
|
|
52
|
+
(def SCHEMA-PATHS [(js/process.cwd) "/app/build/" "/app/" "../"])
|
|
53
|
+
|
|
48
54
|
(def OVS-START-CMD (str "/usr/share/openvswitch/scripts/ovs-ctl start"
|
|
49
55
|
" --system-id=random --no-mlockall --delete-bridges"))
|
|
50
56
|
|
|
@@ -460,7 +466,7 @@ General Options:
|
|
|
460
466
|
"Return a docker/dockerode client object for the docker/podman
|
|
461
467
|
server listening at 'path'."
|
|
462
468
|
[path]
|
|
463
|
-
(P/let [{:keys [
|
|
469
|
+
(P/let [{:keys [warn log]} @ctx]
|
|
464
470
|
(P/catch
|
|
465
471
|
(P/let
|
|
466
472
|
[client (Docker. #js {:socketPath path})
|
|
@@ -468,7 +474,7 @@ General Options:
|
|
|
468
474
|
containers (list-containers client)]
|
|
469
475
|
(log (str "Listening on " path))
|
|
470
476
|
client)
|
|
471
|
-
#(
|
|
477
|
+
#(warn "Could not start docker client on '" path "': " %))))
|
|
472
478
|
|
|
473
479
|
(defn docker-listen
|
|
474
480
|
"Listen for docker events from 'client' that match filter 'filters'.
|
|
@@ -638,9 +644,11 @@ General Options:
|
|
|
638
644
|
(defn arg-checks
|
|
639
645
|
"Check command line arguments. Exit with error if arguments are
|
|
640
646
|
invalid."
|
|
641
|
-
[{:keys [network-file compose-file]}]
|
|
647
|
+
[{:keys [network-file compose-file config-schema orig-config-schema]}]
|
|
642
648
|
(when (and (empty? network-file) (empty? compose-file))
|
|
643
|
-
(fatal 2 "either --network-file or --compose-file is required"))
|
|
649
|
+
(fatal 2 "either --network-file or --compose-file is required"))
|
|
650
|
+
(when (empty? config-schema)
|
|
651
|
+
(fatal 2 "Could not find config-schema" orig-config-schema)))
|
|
644
652
|
|
|
645
653
|
(defn startup-checks
|
|
646
654
|
"Check startup state and exit if openvswitch kernel module is not
|
|
@@ -671,11 +679,13 @@ General Options:
|
|
|
671
679
|
"
|
|
672
680
|
[& args]
|
|
673
681
|
(P/let
|
|
674
|
-
[{:as opts :keys [verbose]} (parse-opts usage args)
|
|
682
|
+
[{:as opts :keys [verbose show-config]} (parse-opts usage args)
|
|
675
683
|
{:keys [log info]} (swap! ctx merge (when verbose {:info Eprintln}))
|
|
676
684
|
opts (merge
|
|
677
685
|
opts
|
|
678
686
|
{:bridge-mode (keyword (:bridge-mode opts))
|
|
687
|
+
:orig-config-schema (:config-schema opts)
|
|
688
|
+
:config-schema (resolve-path (:config-schema opts) SCHEMA-PATHS)
|
|
679
689
|
:network-file (mapcat #(S/split % #":") (:network-file opts))
|
|
680
690
|
:compose-file (mapcat #(S/split % #":") (:compose-file opts))})
|
|
681
691
|
_ (arg-checks opts)
|
|
@@ -689,6 +699,9 @@ General Options:
|
|
|
689
699
|
(interpolate-walk env)
|
|
690
700
|
(check-schema schema verbose)
|
|
691
701
|
(enrich-network-config))
|
|
702
|
+
_ (when show-config
|
|
703
|
+
(println (js/JSON.stringify (->js network-config)))
|
|
704
|
+
(js/process.exit 0))
|
|
692
705
|
|
|
693
706
|
docker (docker-client (:docker-socket opts))
|
|
694
707
|
podman (docker-client (:podman-socket opts))
|
|
@@ -718,6 +731,7 @@ General Options:
|
|
|
718
731
|
(js/process.on "uncaughtException" #(exit-handler %1 %2))
|
|
719
732
|
|
|
720
733
|
(log "Bridge mode:" (name bridge-mode))
|
|
734
|
+
(log (str "Using schema at '" (:config-schema opts) "'"))
|
|
721
735
|
(info (str "Starting network config\n"
|
|
722
736
|
(indent-pprint-str network-config " ")))
|
|
723
737
|
(info (str "Starting network state:\n"
|