Clear History

This commit is contained in:
2023-05-11 17:44:13 +02:00
commit 4acb9b9ae8
77 changed files with 5363 additions and 0 deletions

5
code/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
__pycache__
.venv
.mypy_cache
node_modules

21
code/LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Niccolo Borgioli
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

50
code/Makefile Normal file
View File

@@ -0,0 +1,50 @@
# Cluster
cluster-up::
minikube start --cni calico
cluster-down::
minikube delete
cluster-dash::
minikube dashboard
cluster-env::
# minikube docker-env | source
eval $(minikube docker-env --shell bash)
# Namespace
ns-up::
kubectl create namespace simulator
kubens simulator
ns-down::
kubectl delete namespaces simulator
ns-reset:: ns-down ns-up
# Chaos Mesh
chaos-up::
curl -sSL https://mirrors.chaos-mesh.org/v2.5.1/install.sh | bash
chaos-dash::
minikube service chaos-dashboard -n chaos-mesh
# CRD
crd-up::
kubectl apply -f ./crd/iluzio.yaml
# Docker
docker::
./images/build.sh
# General
dash:
$(MAKE) -j 2 cluster-dash chaos-dash
start:: cluster-up ns-up chaos-up crd-up docker dash
reset:: cluster-down start
operator-up::
poetry install
poetry run kopf run -n simulator src/main.py

4
code/README.md Normal file
View File

@@ -0,0 +1,4 @@
## Structure
- `images`: docker images shared for everything
- `src`: source of the simulator

10
code/Roadmap Normal file
View File

@@ -0,0 +1,10 @@
First:
- api driven
- 2 nodes
- sidecar communication
- block connections with network policies
- ping each other
Second:
- crd driven
- kopf as operator

20
code/config.yaml Normal file
View File

@@ -0,0 +1,20 @@
nodes:
iperf-server:
image: test
command: ["iperf3", "--server", "--one-off"]
iperf-client:
image: test
command: ["iperf3", "--client", "iperf-server", "--time", "10"]
ping-server:
image: test
ping-client:
image: test
command: ["ping", "ping-server"]
events:
- type: network
connect:
- iperf-client
- iperf-server

82
code/crd/iluzio.yaml Normal file
View File

@@ -0,0 +1,82 @@
# Scenario
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: scenarios.iluzio.nicco.io
spec:
scope: Namespaced
group: iluzio.nicco.io
names:
kind: Scenario
plural: scenarios
singular: scenario
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
x-kubernetes-preserve-unknown-fields: true
status:
type: object
x-kubernetes-preserve-unknown-fields: true
---
# Node
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: nodes.iluzio.nicco.io
spec:
scope: Namespaced
group: iluzio.nicco.io
names:
kind: Node
plural: nodes
singular: node
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
x-kubernetes-preserve-unknown-fields: true
status:
type: object
x-kubernetes-preserve-unknown-fields: true
---
# Link
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: links.iluzio.nicco.io
spec:
scope: Namespaced
group: iluzio.nicco.io
names:
kind: Link
plural: links
singular: link
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
x-kubernetes-preserve-unknown-fields: true
status:
type: object
x-kubernetes-preserve-unknown-fields: true

16
code/crd/schema.yml Normal file
View File

@@ -0,0 +1,16 @@
# https://editor.swagger.io/
# https://swagger.io/docs/specification/data-models/data-types/
openapi: 3.0.3
info:
title: Foo
version: 0.0.0
paths: {}
components:
schemas:
Scenario:
type: object
properties:
name:
type: string

12
code/images/build.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
cd "$(dirname "${BASH_SOURCE[0]}")"
eval $(minikube docker-env --shell bash)
function build() {
docker build -t $1 ./$1
}
build "idle" & build "sidecar"
wait

View File

@@ -0,0 +1,5 @@
FROM ubuntu
RUN apt-get update && apt-get install -y iperf3 curl dnsutils iputils-ping netcat python3
CMD [ "sleep", "infinity" ]

View File

@@ -0,0 +1,11 @@
FROM golang:1.20 as builder
WORKDIR /app
COPY . .
RUN go build
FROM ubuntu
RUN apt-get update && apt-get install -y dnsutils
WORKDIR /app
COPY --from=builder /app .
EXPOSE 42069
CMD /app/sidecar

View File

@@ -0,0 +1,3 @@
module iluzio.nicco.io/sidecar
go 1.20

View File

@@ -0,0 +1,44 @@
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"os/exec"
"strings"
)
var SERVICE = os.Getenv("SERVICE")
func getIPs() []string {
out, err := exec.Command("dig", "+short", "+search", SERVICE).Output()
if err != nil {
log.Fatal(err)
return []string{}
}
trimmed := strings.TrimSpace(fmt.Sprintf("%s", out))
if trimmed == "" {
return []string{}
} else {
return strings.Split(trimmed, "\n")
}
}
func main() {
http.HandleFunc("/discoverable", func(w http.ResponseWriter, r *http.Request) {
ips := getIPs()
plain := r.URL.Query().Get("format") == "plain"
if plain {
fmt.Fprintf(w, "%s\n", strings.Join(ips, "\n"))
} else {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(ips)
}
})
log.Fatal(http.ListenAndServe(":42069", nil))
}

1022
code/poetry.lock generated Normal file

File diff suppressed because it is too large Load Diff

2
code/poetry.toml Normal file
View File

@@ -0,0 +1,2 @@
[virtualenvs]
in-project = true

21
code/pyproject.toml Normal file
View File

@@ -0,0 +1,21 @@
[tool.poetry]
name = "sample"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.10"
kubernetes = "^25.3.0"
pyyaml = "^6.0"
kopf = "^1.36.0"
[tool.poetry.group.dev.dependencies]
mypy = "^0.991"
autopep8 = "^2.0.1"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

View File

@@ -0,0 +1,10 @@
FROM node:18
WORKDIR /app
COPY package.json .
RUN npm i
COPY index.js .
EXPOSE 3000
CMD [ "node", "." ]

View File

@@ -0,0 +1,20 @@
import Fastify from 'fastify'
const app = Fastify({ logger: { base: false } })
app.post('/transmit', async (request) => {
request.log.info({ data: request.body })
})
app.get('/time', async () => {
return new Date().toISOString()
})
try {
process.on('SIGINT', () => app.close())
process.on('SIGTERM', () => app.close())
await app.listen({ port: 3000, host: '0.0.0.0' })
} catch (err) {
app.log.error(err)
process.exit(1)
}

View File

@@ -0,0 +1,7 @@
{
"main": "./index.js",
"type": "module",
"dependencies": {
"fastify": "^4.15.0"
}
}

View File

@@ -0,0 +1,380 @@
lockfileVersion: 5.4
specifiers:
fastify: ^4.15.0
dependencies:
fastify: 4.15.0
packages:
/@fastify/ajv-compiler/3.5.0:
resolution: {integrity: sha512-ebbEtlI7dxXF5ziNdr05mOY8NnDiPB1XvAlLHctRt/Rc+C3LCOVW5imUVX+mhvUhnNzmPBHewUkOFgGlCxgdAA==}
dependencies:
ajv: 8.12.0
ajv-formats: 2.1.1
fast-uri: 2.2.0
dev: false
/@fastify/deepmerge/1.3.0:
resolution: {integrity: sha512-J8TOSBq3SoZbDhM9+R/u77hP93gz/rajSA+K2kGyijPpORPWUXHUpTaleoj+92As0S9uPRP7Oi8IqMf0u+ro6A==}
dev: false
/@fastify/error/3.2.0:
resolution: {integrity: sha512-KAfcLa+CnknwVi5fWogrLXgidLic+GXnLjijXdpl8pvkvbXU5BGa37iZO9FGvsh9ZL4y+oFi5cbHBm5UOG+dmQ==}
dev: false
/@fastify/fast-json-stringify-compiler/4.2.0:
resolution: {integrity: sha512-ypZynRvXA3dibfPykQN3RB5wBdEUgSGgny8Qc6k163wYPLD4mEGEDkACp+00YmqkGvIm8D/xYoHajwyEdWD/eg==}
dependencies:
fast-json-stringify: 5.6.2
dev: false
/abort-controller/3.0.0:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
engines: {node: '>=6.5'}
dependencies:
event-target-shim: 5.0.1
dev: false
/abstract-logging/2.0.1:
resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==}
dev: false
/ajv-formats/2.1.1:
resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
peerDependenciesMeta:
ajv:
optional: true
dependencies:
ajv: 8.12.0
dev: false
/ajv/8.12.0:
resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==}
dependencies:
fast-deep-equal: 3.1.3
json-schema-traverse: 1.0.0
require-from-string: 2.0.2
uri-js: 4.4.1
dev: false
/archy/1.0.0:
resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==}
dev: false
/atomic-sleep/1.0.0:
resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==}
engines: {node: '>=8.0.0'}
dev: false
/avvio/8.2.1:
resolution: {integrity: sha512-TAlMYvOuwGyLK3PfBb5WKBXZmXz2fVCgv23d6zZFdle/q3gPjmxBaeuC0pY0Dzs5PWMSgfqqEZkrye19GlDTgw==}
dependencies:
archy: 1.0.0
debug: 4.3.4
fastq: 1.15.0
transitivePeerDependencies:
- supports-color
dev: false
/base64-js/1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
dev: false
/buffer/6.0.3:
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
dependencies:
base64-js: 1.5.1
ieee754: 1.2.1
dev: false
/cookie/0.5.0:
resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
engines: {node: '>= 0.6'}
dev: false
/debug/4.3.4:
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
dependencies:
ms: 2.1.2
dev: false
/event-target-shim/5.0.1:
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
engines: {node: '>=6'}
dev: false
/events/3.3.0:
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
engines: {node: '>=0.8.x'}
dev: false
/fast-content-type-parse/1.0.0:
resolution: {integrity: sha512-Xbc4XcysUXcsP5aHUU7Nq3OwvHq97C+WnbkeIefpeYLX+ryzFJlU6OStFJhs6Ol0LkUGpcK+wL0JwfM+FCU5IA==}
dev: false
/fast-decode-uri-component/1.0.1:
resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==}
dev: false
/fast-deep-equal/3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
dev: false
/fast-json-stringify/5.6.2:
resolution: {integrity: sha512-F6xkRrXvtGbAiDSEI5Rk7qk2P63Y9kc8bO6Dnsd3Rt6sBNr2QxNFWs0JbKftgiyOfGxnJaRoHe4SizCTqeAyrA==}
dependencies:
'@fastify/deepmerge': 1.3.0
ajv: 8.12.0
ajv-formats: 2.1.1
fast-deep-equal: 3.1.3
fast-uri: 2.2.0
rfdc: 1.3.0
dev: false
/fast-querystring/1.1.1:
resolution: {integrity: sha512-qR2r+e3HvhEFmpdHMv//U8FnFlnYjaC6QKDuaXALDkw2kvHO8WDjxH+f/rHGR4Me4pnk8p9JAkRNTjYHAKRn2Q==}
dependencies:
fast-decode-uri-component: 1.0.1
dev: false
/fast-redact/3.1.2:
resolution: {integrity: sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw==}
engines: {node: '>=6'}
dev: false
/fast-uri/2.2.0:
resolution: {integrity: sha512-cIusKBIt/R/oI6z/1nyfe2FvGKVTohVRfvkOhvx0nCEW+xf5NoCXjAHcWp93uOUBchzYcsvPlrapAdX1uW+YGg==}
dev: false
/fastify/4.15.0:
resolution: {integrity: sha512-m/CaRN8nf5uyYdrDe2qqq+0z3oGyE+A++qlKQoLJTI4WI0nWK9D6R3FxXQ3MVwt/md977GMR4F43pE9oqrS2zw==}
dependencies:
'@fastify/ajv-compiler': 3.5.0
'@fastify/error': 3.2.0
'@fastify/fast-json-stringify-compiler': 4.2.0
abstract-logging: 2.0.1
avvio: 8.2.1
fast-content-type-parse: 1.0.0
find-my-way: 7.6.0
light-my-request: 5.9.1
pino: 8.11.0
process-warning: 2.1.0
proxy-addr: 2.0.7
rfdc: 1.3.0
secure-json-parse: 2.7.0
semver: 7.3.8
tiny-lru: 10.3.0
transitivePeerDependencies:
- supports-color
dev: false
/fastq/1.15.0:
resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==}
dependencies:
reusify: 1.0.4
dev: false
/find-my-way/7.6.0:
resolution: {integrity: sha512-H7berWdHJ+5CNVr4ilLWPai4ml7Y2qAsxjw3pfeBxPigZmaDTzF0wjJLj90xRCmGcWYcyt050yN+34OZDJm1eQ==}
engines: {node: '>=14'}
dependencies:
fast-deep-equal: 3.1.3
fast-querystring: 1.1.1
safe-regex2: 2.0.0
dev: false
/forwarded/0.2.0:
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
engines: {node: '>= 0.6'}
dev: false
/ieee754/1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
dev: false
/ipaddr.js/1.9.1:
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
engines: {node: '>= 0.10'}
dev: false
/json-schema-traverse/1.0.0:
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
dev: false
/light-my-request/5.9.1:
resolution: {integrity: sha512-UT7pUk8jNCR1wR7w3iWfIjx32DiB2f3hFdQSOwy3/EPQ3n3VocyipUxcyRZR0ahoev+fky69uA+GejPa9KuHKg==}
dependencies:
cookie: 0.5.0
process-warning: 2.1.0
set-cookie-parser: 2.6.0
dev: false
/lru-cache/6.0.0:
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
engines: {node: '>=10'}
dependencies:
yallist: 4.0.0
dev: false
/ms/2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
dev: false
/on-exit-leak-free/2.1.0:
resolution: {integrity: sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==}
dev: false
/pino-abstract-transport/1.0.0:
resolution: {integrity: sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==}
dependencies:
readable-stream: 4.3.0
split2: 4.2.0
dev: false
/pino-std-serializers/6.1.0:
resolution: {integrity: sha512-KO0m2f1HkrPe9S0ldjx7za9BJjeHqBku5Ch8JyxETxT8dEFGz1PwgrHaOQupVYitpzbFSYm7nnljxD8dik2c+g==}
dev: false
/pino/8.11.0:
resolution: {integrity: sha512-Z2eKSvlrl2rH8p5eveNUnTdd4AjJk8tAsLkHYZQKGHP4WTh2Gi1cOSOs3eWPqaj+niS3gj4UkoreoaWgF3ZWYg==}
hasBin: true
dependencies:
atomic-sleep: 1.0.0
fast-redact: 3.1.2
on-exit-leak-free: 2.1.0
pino-abstract-transport: 1.0.0
pino-std-serializers: 6.1.0
process-warning: 2.1.0
quick-format-unescaped: 4.0.4
real-require: 0.2.0
safe-stable-stringify: 2.4.3
sonic-boom: 3.3.0
thread-stream: 2.3.0
dev: false
/process-warning/2.1.0:
resolution: {integrity: sha512-9C20RLxrZU/rFnxWncDkuF6O999NdIf3E1ws4B0ZeY3sRVPzWBMsYDE2lxjxhiXxg464cQTgKUGm8/i6y2YGXg==}
dev: false
/process/0.11.10:
resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
engines: {node: '>= 0.6.0'}
dev: false
/proxy-addr/2.0.7:
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
engines: {node: '>= 0.10'}
dependencies:
forwarded: 0.2.0
ipaddr.js: 1.9.1
dev: false
/punycode/2.3.0:
resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
engines: {node: '>=6'}
dev: false
/quick-format-unescaped/4.0.4:
resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==}
dev: false
/readable-stream/4.3.0:
resolution: {integrity: sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
abort-controller: 3.0.0
buffer: 6.0.3
events: 3.3.0
process: 0.11.10
dev: false
/real-require/0.2.0:
resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==}
engines: {node: '>= 12.13.0'}
dev: false
/require-from-string/2.0.2:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
dev: false
/ret/0.2.2:
resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==}
engines: {node: '>=4'}
dev: false
/reusify/1.0.4:
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
dev: false
/rfdc/1.3.0:
resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==}
dev: false
/safe-regex2/2.0.0:
resolution: {integrity: sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==}
dependencies:
ret: 0.2.2
dev: false
/safe-stable-stringify/2.4.3:
resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==}
engines: {node: '>=10'}
dev: false
/secure-json-parse/2.7.0:
resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==}
dev: false
/semver/7.3.8:
resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==}
engines: {node: '>=10'}
hasBin: true
dependencies:
lru-cache: 6.0.0
dev: false
/set-cookie-parser/2.6.0:
resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==}
dev: false
/sonic-boom/3.3.0:
resolution: {integrity: sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g==}
dependencies:
atomic-sleep: 1.0.0
dev: false
/split2/4.2.0:
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
engines: {node: '>= 10.x'}
dev: false
/thread-stream/2.3.0:
resolution: {integrity: sha512-kaDqm1DET9pp3NXwR8382WHbnpXnRkN9xGN9dQt3B2+dmXiW8X1SOwmFOxAErEQ47ObhZ96J6yhZNXuyCOL7KA==}
dependencies:
real-require: 0.2.0
dev: false
/tiny-lru/10.3.0:
resolution: {integrity: sha512-vTKRT2AEO1sViFDWAIzZVpV8KURCaMtnHa4RZB3XqtYLbrTO/fLDXKPEX9kVWq9u+nZREkwakbcmzGgvJm8QKA==}
engines: {node: '>=12'}
dev: false
/uri-js/4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
dependencies:
punycode: 2.3.0
dev: false
/yallist/4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
dev: false

View File

@@ -0,0 +1,9 @@
#!/bin/bash
cd "$(dirname "${BASH_SOURCE[0]}")"
eval $(minikube docker-env --shell bash)
docker build -t base-station ./base
docker build -t satellite ./sat
kubectl apply -f ./scenario.yaml

View File

@@ -0,0 +1,10 @@
FROM node:18
WORKDIR /app
COPY package.json .
RUN npm install
COPY index.js .
STOPSIGNAL SIGTERM
CMD [ "node", "." ]

View File

@@ -0,0 +1,31 @@
import pino from 'pino'
const logger = pino({ base: false })
const interval = setInterval(async () => {
const ips = await fetch('http://localhost:42069/discoverable')
.then((res) => res.json())
.catch(() => [])
if (!ips.length) {
logger.info('no peers found')
return
}
for (const ip of ips) {
// Sync some data
// Don't await on purpose
fetch(`http://${ip}:3000/time`)
.then((res) => res.text())
.then((time) => logger.info({ peer: ip }, `time from peer: ${time}`))
.catch(logger.error)
fetch(`http://${ip}:3000/transmit`, { method: 'POST', body: `Observation: ${Math.random()}` }).catch(logger.error)
}
}, 1000)
function exit() {
clearInterval(interval)
process.exit(0)
}
process.on('SIGINT', exit)
process.on('SIGTERM', exit)

View File

@@ -0,0 +1,7 @@
{
"main": "./index.js",
"type": "module",
"dependencies": {
"pino": "^8.11.0"
}
}

View File

@@ -0,0 +1,133 @@
lockfileVersion: 5.4
specifiers:
pino: ^8.11.0
dependencies:
pino: 8.11.0
packages:
/abort-controller/3.0.0:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
engines: {node: '>=6.5'}
dependencies:
event-target-shim: 5.0.1
dev: false
/atomic-sleep/1.0.0:
resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==}
engines: {node: '>=8.0.0'}
dev: false
/base64-js/1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
dev: false
/buffer/6.0.3:
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
dependencies:
base64-js: 1.5.1
ieee754: 1.2.1
dev: false
/event-target-shim/5.0.1:
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
engines: {node: '>=6'}
dev: false
/events/3.3.0:
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
engines: {node: '>=0.8.x'}
dev: false
/fast-redact/3.1.2:
resolution: {integrity: sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw==}
engines: {node: '>=6'}
dev: false
/ieee754/1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
dev: false
/on-exit-leak-free/2.1.0:
resolution: {integrity: sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==}
dev: false
/pino-abstract-transport/1.0.0:
resolution: {integrity: sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==}
dependencies:
readable-stream: 4.3.0
split2: 4.2.0
dev: false
/pino-std-serializers/6.1.0:
resolution: {integrity: sha512-KO0m2f1HkrPe9S0ldjx7za9BJjeHqBku5Ch8JyxETxT8dEFGz1PwgrHaOQupVYitpzbFSYm7nnljxD8dik2c+g==}
dev: false
/pino/8.11.0:
resolution: {integrity: sha512-Z2eKSvlrl2rH8p5eveNUnTdd4AjJk8tAsLkHYZQKGHP4WTh2Gi1cOSOs3eWPqaj+niS3gj4UkoreoaWgF3ZWYg==}
hasBin: true
dependencies:
atomic-sleep: 1.0.0
fast-redact: 3.1.2
on-exit-leak-free: 2.1.0
pino-abstract-transport: 1.0.0
pino-std-serializers: 6.1.0
process-warning: 2.1.0
quick-format-unescaped: 4.0.4
real-require: 0.2.0
safe-stable-stringify: 2.4.3
sonic-boom: 3.3.0
thread-stream: 2.3.0
dev: false
/process-warning/2.1.0:
resolution: {integrity: sha512-9C20RLxrZU/rFnxWncDkuF6O999NdIf3E1ws4B0ZeY3sRVPzWBMsYDE2lxjxhiXxg464cQTgKUGm8/i6y2YGXg==}
dev: false
/process/0.11.10:
resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
engines: {node: '>= 0.6.0'}
dev: false
/quick-format-unescaped/4.0.4:
resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==}
dev: false
/readable-stream/4.3.0:
resolution: {integrity: sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
abort-controller: 3.0.0
buffer: 6.0.3
events: 3.3.0
process: 0.11.10
dev: false
/real-require/0.2.0:
resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==}
engines: {node: '>= 12.13.0'}
dev: false
/safe-stable-stringify/2.4.3:
resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==}
engines: {node: '>=10'}
dev: false
/sonic-boom/3.3.0:
resolution: {integrity: sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g==}
dependencies:
atomic-sleep: 1.0.0
dev: false
/split2/4.2.0:
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
engines: {node: '>= 10.x'}
dev: false
/thread-stream/2.3.0:
resolution: {integrity: sha512-kaDqm1DET9pp3NXwR8382WHbnpXnRkN9xGN9dQt3B2+dmXiW8X1SOwmFOxAErEQ47ObhZ96J6yhZNXuyCOL7KA==}
dependencies:
real-require: 0.2.0
dev: false

View File

@@ -0,0 +1,72 @@
apiVersion: iluzio.nicco.io/v1
kind: Scenario
metadata:
name: sat-base
spec:
events:
# Setup
- offset: 0
resource: node
action: create
id: base0
spec:
image: base-station
airGapped: false
- offset: 0
resource: node
action: create
id: base1
spec:
image: base-station
- offset: 0
resource: node
action: create
id: sat0
spec:
image: satellite
# Links
- offset: 15
resource: link
action: create
from: base0
to: sat0
direction: bi
spec:
bandwidth:
rate: 10mbps
limit: 2000000000
buffer: 200000
- offset: 45
resource: link
action: delete
from: base0
to: sat0
direction: bi
- offset: 30
resource: link
action: create
from: base1
to: sat0
direction: bi
spec:
bandwidth:
rate: 10mbps
limit: 2000000000
buffer: 200000
- offset: 60
resource: link
action: delete
from: base1
to: sat0
direction: bi
# End
- offset: 80
resource: scenario
action: end

8
code/src/k8s.py Normal file
View File

@@ -0,0 +1,8 @@
from kubernetes import client, config
config.load_kube_config()
core = client.CoreV1Api()
apps = client.AppsV1Api()
crd = client.CustomObjectsApi()
networking = client.NetworkingV1Api()

99
code/src/kinds/link.py Normal file
View File

@@ -0,0 +1,99 @@
import kopf
import logging
import templates
import k8s
import utils
RESOURCE = 'links.' + utils.GROUP
def get_link_metadata(body):
try:
name = body['metadata']['name']
sender = body['spec']['from']
receiver = body['spec']['to']
direction = body['spec']['direction']
if direction not in ['uni', 'bi']:
raise kopf.PermanentError(f'Invalid direction "{direction}" for link "{name}"')
unidirectional = direction == 'uni'
namespace = body['metadata']['namespace']
return namespace, name, sender, receiver, unidirectional
except KeyError as e:
raise kopf.PermanentError(f'Invalid link "{name}"') from e
def build_labels(node, value):
return {"metadata": {"labels": {
f"send-node-{node}": value,
f"receive-node-{node}": value,
}}}
@kopf.on.create(RESOURCE)
def create(body, **kwargs) -> None:
namespace, name, sender, receiver, unidirectional = get_link_metadata(body)
logging.info(f'Link "{name}" ({sender} -> {receiver}) is created on namespace "{namespace}"')
logging.debug(body)
# Chaos experiment
logging.info(
f'Creating link "{name}" ({sender} -> {receiver}) on namespace "{namespace}" with parameters "{body.spec}"')
direction = 'to' if unidirectional else 'both'
crd = templates.chaos_link(name=name, namespace=namespace, sender=sender, receiver=receiver, direction=direction)
# Check if there is a fault to apply
has_fault = False
if 'bandwidth' in body.spec:
has_fault = True
crd['spec']['action'] = 'bandwidth'
crd['spec']['bandwidth'] = body.spec['bandwidth']
else:
crd['spec']['action'] = 'netem'
for action in ['delay', 'loss', 'duplicate', 'corrupt']:
if action in body.spec:
has_fault = True
crd['spec'][action] = body.spec[action]
if has_fault:
# Only create the chaos experiment if there is a fault to apply
group, version = crd['apiVersion'].split('/')
kopf.adopt(crd)
k8s.crd.create_namespaced_custom_object(group, version, namespace, 'networkchaos', crd)
# Label the pods to enable the service and exception to the network policy
pod_sender = k8s.core.list_namespaced_pod(namespace=namespace, label_selector=f'node={sender}')
for pod in pod_sender.items:
patch = build_labels(receiver, "enabled")
if unidirectional:
del patch["metadata"]["labels"][f"receive-node-{receiver}"]
k8s.core.patch_namespaced_pod(pod.metadata.name, namespace, patch)
pod_receiver = k8s.core.list_namespaced_pod(namespace=namespace, label_selector=f'node={receiver}')
for pod in pod_receiver.items:
patch = build_labels(sender, "enabled")
if unidirectional:
del patch["metadata"]["labels"][f"send-node-{sender}"]
k8s.core.patch_namespaced_pod(pod.metadata.name, namespace, patch)
@ kopf.on.delete(RESOURCE)
def delete(body, **kwargs):
namespace, name, sender, receiver, unidirectional = get_link_metadata(body)
logging.info(f'Link "{name}" ({sender} -> {receiver}) is deleted on namespace "{namespace}"')
logging.debug(body)
# Reset labels
pod_sender = k8s.core.list_namespaced_pod(namespace=namespace, label_selector=f'node={sender}')
for pod in pod_sender.items:
patch = build_labels(receiver, None)
if unidirectional:
del patch["metadata"]["labels"][f"receive-node-{receiver}"]
k8s.core.patch_namespaced_pod(pod.metadata.name, namespace, patch)
pod_receiver = k8s.core.list_namespaced_pod(namespace=namespace, label_selector=f'node={receiver}')
for pod in pod_receiver.items:
patch = build_labels(sender, None)
if unidirectional:
del patch["metadata"]["labels"][f"send-node-{sender}"]
k8s.core.patch_namespaced_pod(pod.metadata.name, namespace, patch)

55
code/src/kinds/node.py Normal file
View File

@@ -0,0 +1,55 @@
import kopf
import logging
import json
import templates
import k8s
import utils
RESOURCE = 'nodes.' + utils.GROUP
@kopf.on.create(RESOURCE)
def create(body, **kwargs):
name = body['metadata']['name']
namespace = body['metadata']['namespace']
logging.info(f'Node "{name}" is created on namespace "{namespace}"')
logging.debug(body)
try:
resources = json.dumps(body['spec']['resources'])
except KeyError:
resources = '{}'
try:
airGapped = body['spec']['airGapped'] == True
except KeyError:
airGapped = True
deployment, service, policy = templates.native_node(id=name, image=body['spec']['image'], resources=resources)
if not airGapped:
obj = {"ipBlock": {
"cidr": "0.0.0.0/0",
"except": ["10.0.0.0/8"]
}}
policy['spec']['ingress'].append({'from': [obj]})
policy['spec']['egress'].append({'to': [obj]})
logging.debug(deployment)
logging.debug(service)
logging.debug(policy)
kopf.adopt(deployment)
kopf.adopt(service)
kopf.adopt(policy)
k8s.apps.create_namespaced_deployment(namespace, deployment)
k8s.core.create_namespaced_service(namespace, service)
k8s.networking.create_namespaced_network_policy(namespace, policy)
@kopf.on.delete(RESOURCE)
def delete(body, **kwargs):
name = body['metadata']['name']
logging.info(f'Node "{name}" is deleted')
logging.debug(body)

141
code/src/kinds/scenario.py Normal file
View File

@@ -0,0 +1,141 @@
import asyncio
import logging
import kopf
import k8s
import templates
import utils
RESOURCE = 'scenarios.' + utils.GROUP
@kopf.on.create(RESOURCE)
def create(body, patch, **kwargs):
name = body['metadata']['name']
namespace = body['metadata']['namespace']
logging.info(f'scenario "{name}" is created on namespace "{namespace}"')
@kopf.on.delete(RESOURCE)
def delete(body, **kwargs):
name = body['metadata']['name']
logging.info(f'scenario "{name}" is deleted')
@kopf.daemon(RESOURCE, cancellation_timeout=1)
async def daemon(meta, status, spec, **kwargs):
try:
while True:
try:
if status['ended']:
return
except KeyError:
pass
name = meta['name']
namespace = meta['namespace']
def patch(body):
k8s.crd.patch_namespaced_custom_object(
utils.GROUP, utils.VERSION, namespace, 'scenarios', name, {'status': body})
def with_prefix(id: str) -> str:
return f'{name}-{id}'
now = utils.timestamp_ms()
try:
started = status['started']
except KeyError:
patch({'started': now})
logging.info('waiting for scenario to start...')
await asyncio.sleep(0.1)
continue
# Time since the scenario started
elapsed = now - started
logging.info(f'elapsed: {elapsed}')
logging.debug(status)
# Calculate when the next run should be executed, based on future events
next_run = None
for i, event in enumerate(spec['events']):
offset = (event['offset'] or 0) * 1000
delta = offset - elapsed
if delta > 0:
if next_run is None or delta < next_run:
next_run = delta
continue
try:
s = status['events'][str(i)]
executed = s['executed']
except KeyError:
executed = False
if not executed:
# Execute event
logging.info(f'executing event {i}')
patch({'events': {i: {'executed': True, 'timestamp': now}}})
action = event['action']
if action == 'end':
# End scenario
logging.info(f'ending scenario {event}')
patch({'ended': now})
return
# NODE
elif event['resource'] == 'node':
ID = with_prefix(event['id'])
if action == 'create':
# Create node
logging.info(f'creating node {event}')
body = templates.iluzio_node(id=ID)
body['spec'] = event['spec']
kopf.adopt(body)
k8s.crd.create_namespaced_custom_object(
utils.GROUP, utils.VERSION, namespace, 'nodes', body)
elif action == 'delete':
# Delete node
logging.info(f'deleting node {event}')
k8s.crd.delete_namespaced_custom_object(
utils.GROUP, utils.VERSION, namespace, 'nodes', ID)
else:
logging.error(f'unknown action {action}')
# LINK
elif event['resource'] == 'link':
ID = '-'.join(map(lambda x: event[x], ['from', 'to', 'direction']))
ID = with_prefix(ID)
if action == 'create':
# Create links
logging.info(f'creating link {event}')
body = templates.iluzio_link(id=ID)
body['spec'].update(event['spec'])
body['spec']['from'] = with_prefix(event['from'])
body['spec']['to'] = with_prefix(event['to'])
body['spec']['direction'] = event['direction']
kopf.adopt(body)
k8s.crd.create_namespaced_custom_object(
utils.GROUP, utils.VERSION, namespace, 'links', body)
elif action == 'delete':
# Delete link
logging.info(f'deleting link {event}')
k8s.crd.delete_namespaced_custom_object(
utils.GROUP, utils.VERSION, namespace, 'links', ID)
else:
logging.error(f'unknown action {action}')
else:
logging.error(f'unknown resource {event["resource"]}')
if next_run is None:
logging.error('no next run could be calculated')
return
logging.info(f'waiting for next run in {next_run} ms')
await asyncio.sleep(next_run / 1000)
except asyncio.CancelledError:
logging.info('cancellation requested')

3
code/src/main.py Normal file
View File

@@ -0,0 +1,3 @@
import kinds.node
import kinds.link
import kinds.scenario

51
code/src/templates.py Normal file
View File

@@ -0,0 +1,51 @@
from os import path
from string import Template
import yaml
templates_dir = path.join(path.dirname(__file__), 'templates')
def load(name: str, variables):
"""
Load a template file and replace the placeholders with the given.
"""
file = path.join(templates_dir, name)
with open(file, 'r') as template:
contents = template.read()
replaced = Template(contents).substitute(**variables)
return list(yaml.safe_load_all(replaced))
# Iluzio
def iluzio_node(*, id: str):
"""
Load the iluzio node template.
This includes the deployment, service and network policy.
"""
return load('iluzio/node.yaml', {'id': id})[0]
def iluzio_link(*, id: str):
"""
Load the iluzio link template.
"""
return load('iluzio/link.yaml', {'id': id})[0]
# Native
def native_node(*, id: str, image: str, resources: str) -> tuple[str, str, str]:
"""
Load the node template.
This includes the deployment, service and network policy.
"""
return load('native/node.yaml', {'id': id, 'image': image, 'resources': resources})
# Chaos
def chaos_link(*, name: str, namespace: str, sender: str, receiver: str, direction: str):
"""
Load the chaos link template.
This includes the link and the service.
"""
return load('chaos/link.yaml', {'name': name, 'namespace': namespace, 'sender': sender, 'receiver': receiver, 'direction': direction})[0]

View File

@@ -0,0 +1,19 @@
kind: NetworkChaos
apiVersion: chaos-mesh.org/v1alpha1
metadata:
name: ${name}
spec:
selector:
namespaces:
- ${namespace}
labelSelectors:
node: ${sender}
mode: all
target:
selector:
namespaces:
- ${namespace}
labelSelectors:
node: ${receiver}
mode: all
direction: ${direction}

View File

@@ -0,0 +1,110 @@
kind: NetworkChaos
apiVersion: chaos-mesh.org/v1alpha1
metadata:
namespace: simulator
name: test
spec:
selector:
namespaces:
- simulator
labelSelectors:
node: a
mode: all
action: bandwidth
direction: to
target:
selector:
namespaces:
- simulator
labelSelectors:
node: b
mode: all
bandwidth:
rate: 10mbps
limit: 2000000000
buffer: 200000
delay:
latency: 50ms
correlation: '25'
jitter: 25ms
# loss:
# loss: '1'
# duplicate:
# duplicate: '1'
# corrupt:
# corrupt: '1'
# ---
# kind: NetworkChaos
# apiVersion: chaos-mesh.org/v1alpha1
# metadata:
# namespace: simulator
# name: link-a-b
# spec:
# selector:
# namespaces:
# - simulator
# labelSelectors:
# node: a
# mode: all
# action: partition
# direction: to
# target:
# selector:
# namespaces:
# - simulator
# labelSelectors:
# node: b
# mode: all
# ---
# kind: NetworkChaos
# apiVersion: chaos-mesh.org/v1alpha1
# metadata:
# namespace: simulator
# name: test
# spec:
# selector:
# namespaces:
# - simulator
# labelSelectors:
# node: a
# mode: all
# action: delay
# delay:
# latency: 10ms
# correlation: '0'
# jitter: 1ms
# direction: both
# target:
# selector:
# namespaces:
# - simulator
# labelSelectors:
# node: b
# mode: all
# ---
# kind: NetworkChaos
# apiVersion: chaos-mesh.org/v1alpha1
# metadata:
# namespace: simulator
# name: band-b
# spec:
# selector:
# namespaces:
# - simulator
# labelSelectors:
# node: a
# mode: all
# action: bandwidth
# bandwidth:
# rate: 10mbps
# limit: 2000000000
# buffer: 1500
# direction: both
# target:
# selector:
# namespaces:
# - simulator
# labelSelectors:
# node: b
# mode: all

View File

@@ -0,0 +1,25 @@
apiVersion: iluzio.nicco.io/v1
kind: Link
metadata:
name: test-link
spec:
from: a
to: b
# direction: uni
direction: bi
# bandwidth:
# rate: 1kbps
# # limit: 20971520
# limit: 10000
# buffer: 5000
# delay:
# latency: 100ms
# correlation: '0'
# jitter: 50ms
# loss:
# loss: '0.5'
# correlation: '100'
# duplicate:
# duplicate: '1'
# corrupt:
# corrupt: '1'

View File

@@ -0,0 +1,11 @@
apiVersion: iluzio.nicco.io/v1
kind: Node
metadata:
name: b
spec:
image: idle
resources:
limits:
memory: '128Mi'
cpu: '500m'
ephemeral-storage: '4Gi'

View File

@@ -0,0 +1,25 @@
kind: NetworkChaos
apiVersion: chaos-mesh.org/v1alpha1
metadata:
namespace: simulator
name: band-b
spec:
selector:
namespaces:
- simulator
labelSelectors:
node: a
mode: all
action: bandwidth
bandwidth:
rate: 10gbps
limit: 2000000000
buffer: 1500
direction: both
target:
selector:
namespaces:
- simulator
labelSelectors:
node: b
mode: all

View File

@@ -0,0 +1,47 @@
apiVersion: chaos-mesh.org/v1alpha1
kind: Workflow
metadata:
name: test
spec:
entry: entry
templates:
- name: entry
templateType: Serial
children:
- delay
- loss
- name: delay
templateType: NetworkChaos
networkChaos:
action: delay
selector:
namespaces:
- simulator
mode: all
target:
selector:
namespaces:
- simulator
mode: all
direction: both
delay:
latency: '90ms'
correlation: '25'
jitter: '1ms'
- name: loss
templateType: NetworkChaos
networkChaos:
action: delay
selector:
namespaces:
- simulator
mode: all
target:
selector:
namespaces:
- simulator
mode: all
direction: both
loss:
loss: '25'
correlation: '25'

View File

@@ -0,0 +1,5 @@
apiVersion: iluzio.nicco.io/v1
kind: Link
metadata:
name: ${id}
spec: {}

View File

@@ -0,0 +1,5 @@
apiVersion: iluzio.nicco.io/v1
kind: Node
metadata:
name: ${id}
spec: {}

View File

@@ -0,0 +1,81 @@
# Simulation node with sidecar
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${id}
spec:
replicas: 1
selector:
matchLabels:
node: ${id}
template:
metadata:
name: pod-${id}
labels:
node: ${id}
spec:
dnsPolicy: ClusterFirst
containers:
# Image
- name: app
image: ${image}
imagePullPolicy: Never
resources: ${resources}
# Sidecar
- name: sidecar
image: sidecar
imagePullPolicy: Never
env:
- name: SERVICE
value: ${id}
resources: {}
---
# Service that makes other nodes discoverable to this node
apiVersion: v1
kind: Service
metadata:
name: ${id}
spec:
clusterIP: None
selector:
receive-node-${id}: enabled
---
# This network policy is to deny all traffic from and to another namespace
# https://github.com/ahmetb/kubernetes-network-policy-recipes/blob/master/04-deny-traffic-from-other-namespaces.md
# Exceptions is traffic to and from kube-system namespace. This is needed for the DNS resolution of services.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: ${id}
spec:
podSelector:
matchLabels:
node: ${id}
ingress:
# Internal DNS
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
# All the pods in the same namespace
- from:
- podSelector:
matchLabels:
send-node-${id}: enabled
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
- to:
- podSelector:
matchLabels:
receive-node-${id}: enabled

8
code/src/utils.py Normal file
View File

@@ -0,0 +1,8 @@
import time
GROUP = 'iluzio.nicco.io'
VERSION = 'v1'
def timestamp_ms() -> int:
return int(time.time() * 1000)