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.
Files changed (52) hide show
  1. package/Dockerfile +2 -1
  2. package/README.md +18 -14
  3. package/examples/net2dot.yaml +2 -2
  4. package/examples/test4-multiple/modes/all/deps +1 -0
  5. package/examples/test4-multiple/{base-compose.yaml → modes/base/compose.yaml} +3 -3
  6. package/examples/test4-multiple/{node1-compose.yaml → modes/node1/compose.yaml} +0 -3
  7. package/examples/test4-multiple/modes/node1/deps +1 -0
  8. package/examples/test4-multiple/{nodes2-compose.yaml → modes/nodes2/compose.yaml} +0 -5
  9. package/examples/test4-multiple/modes/nodes2/deps +1 -0
  10. package/examples/test4-multiple/modes/web/compose.yaml +5 -0
  11. package/examples/test4-multiple/modes/web/deps +1 -0
  12. package/examples/test6-cfn.yaml +1 -2
  13. package/mdc +108 -0
  14. package/net2dot +1 -1
  15. package/net2dot.cljs +113 -0
  16. package/package.json +6 -1
  17. package/scripts/copy.sh +48 -0
  18. package/scripts/wait.sh +73 -0
  19. package/shadow-cljs.edn +1 -1
  20. package/src/conlink/addrs.cljc +3 -0
  21. package/src/conlink/core.cljs +21 -7
  22. package/src/conlink/util.cljs +13 -2
  23. package/TODO +0 -34
  24. package/examples/test4-multiple/all-compose.yaml +0 -5
  25. package/examples/test4-multiple/web-network.yaml +0 -2
  26. package/host-build.yaml +0 -1
  27. package/inspect.json +0 -210
  28. package/notes.txt +0 -82
  29. package/old/Dockerfile.bak +0 -26
  30. package/old/add-link.sh +0 -82
  31. package/old/conlink +0 -12
  32. package/old/conlink.cljs +0 -131
  33. package/old/dot_gitignore +0 -1
  34. package/old/examples/test2-compose.yaml +0 -32
  35. package/old/examples/test2-network.yaml +0 -42
  36. package/old/move-link.sh +0 -108
  37. package/old/net2dot.py +0 -122
  38. package/old/notes-old.txt +0 -97
  39. package/old/package.json +0 -16
  40. package/old/schema.yaml +0 -138
  41. package/old/schema.yaml.bak +0 -76
  42. package/old/test2b-compose.yaml +0 -18
  43. package/old/veth-link.sh +0 -96
  44. package/schema-ish.yaml +0 -29
  45. package/src/conlink/net2dot.cljs +0 -158
  46. package/tests/invalid-schema-1.yaml +0 -6
  47. package/tests/invalid-schema-2.yaml +0 -6
  48. package/tests/invalid-schema-3.yaml +0 -17
  49. package/tests/invalid-schema-4.yaml +0 -14
  50. package/tests/invalid-schema-5.yaml +0 -12
  51. package/tests/invalid-schema-6.yaml +0 -12
  52. 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 schema.yaml /app/
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
- echo "COMPOSE_FILE=examples/test4-multiple/base-compose.yaml:examples/test4-multiple/node1-compose.yaml" > .env
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
- echo "COMPOSE_FILE=examples/test4-multiple/base-compose.yaml:examples/test4-multiple/node1-compose.yaml:examples/test4-multiple/nodes2-compose.yaml" > .env
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
- echo "COMPOSE_FILE=examples/test4-multiple/base-compose.yaml:examples/test4-multiple/node1-compose.yaml:examples/test4-multiple/nodes2-compose.yaml:examples/test4-multiple/all-compose.yaml" > .env
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). The `net2dot`
457
- script supports `--compose-file` and `--network-file` command line
458
- options. To render the network configuration for example test1, run
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
- ./net2dot --compose-file examples/test1-compose.yaml > examples/data.dot
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
- ./net2dot --network-file examples/net2dot.yaml > examples/data.dot
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
@@ -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: "${REMOTE1}"}
19
- - {type: geneve, bridge: s3, vni: 1002, remote: "${REMOTE2}"}
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
- - ../../:/test
22
+ - ./:/test
23
23
  working_dir: /test
24
- command: /app/build/conlink.js --compose-file ${COMPOSE_FILE:-examples/test4-multiple/base-compose.yaml}
24
+ command: /app/build/conlink.js --compose-file ${COMPOSE_FILE:?COMPOSE_FILE must be set}
25
25
 
@@ -5,9 +5,6 @@ x-network:
5
5
  - {service: r0, bridge: s1, dev: e1, ip: "10.1.0.100/24"}
6
6
 
7
7
  services:
8
- network:
9
- command: /app/build/conlink.js --compose-file ${COMPOSE_FILE:-examples/test4-multiple/node1-compose.yaml}
10
-
11
8
  node1:
12
9
  image: alpine
13
10
  network_mode: none
@@ -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,5 @@
1
+ version: "2.4"
2
+
3
+ x-network:
4
+ commands:
5
+ - {service: r0, command: "python3 -m http.server 80"}
@@ -0,0 +1 @@
1
+ base
@@ -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:config-and-cljs-refactor
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
@@ -8,4 +8,4 @@ die() { echo >&2 "${*}"; exit 1; }
8
8
 
9
9
  [ -e "${NBB}" ] || die "Missing ${NBB}. Maybe run 'npm install' in ${TOP_DIR}?"
10
10
 
11
- exec ${NBB} -cp "${TOP_DIR}/src" -m conlink.net2dot/main "${@}"
11
+ exec ${NBB} "${TOP_DIR}/net2dot.cljs" "${@}"
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.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": {
@@ -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
@@ -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}
@@ -1,3 +1,6 @@
1
+ ;; Copyright (c) 2023, Viasat, Inc
2
+ ;; Licensed under MPL 2.0
3
+
1
4
  (ns conlink.addrs
2
5
  (:require [clojure.string :as string]))
3
6
 
@@ -1,4 +1,5 @@
1
- #!/usr/bin/env nbb
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 [error log]} @ctx]
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
- #(error "Could not start docker client on '" path "': " %))))
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"