From 25d4bfa0ee76c80cd9c546200caeebda17cca0e5 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Thu, 18 Dec 2025 13:34:07 +0100 Subject: [PATCH 01/84] f-1: clean: delete keep files #1 Signed-off-by: GnomeZworc --- cmd/agent/.keep | 0 internal/.keep | 0 pkg/.keep | 0 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 cmd/agent/.keep delete mode 100644 internal/.keep delete mode 100644 pkg/.keep diff --git a/cmd/agent/.keep b/cmd/agent/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/internal/.keep b/internal/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/pkg/.keep b/pkg/.keep deleted file mode 100644 index e69de29..0000000 From f67376964a6eae797a8c92c392ddb3eaa6addb69 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Thu, 18 Dec 2025 14:06:02 +0100 Subject: [PATCH 02/84] f-1: scripts: start deploy script #1 Signed-off-by: GnomeZworc --- scripts/deploy.sh | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 scripts/deploy.sh diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100644 index 0000000..9fcd1dd --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1,38 @@ +#!/bin/bash +set -e + +SED_PARAM="" +unameOut="$(uname -s)" +case "${unameOut}" in + Linux*) SED_PARAM=" -i ";; + Darwin*) SED_PARAM=" -i '' ";; + *) exit 1 +esac + +SCRIPT_PATH="scripts/deploy.sh" + +check_latest_script () { + REMOTE_URL="${1}" + LOCAL_PATH="${2}" + + REMOTE=$(curl --silent "${REMOTE_URL}" | sha256sum) + LOCAL=$(cat ${LOCAL_PATH} | sha256sum) + + [[ "${REMOTE}" == "${LOCAL}" ]] \ + && echo "both are the same" \ + || echo "not the same" +} + +main () { + [[ -f ./libs/shflags ]] && . ./libs/shflags || eval "$(curl --silent https://git.g3e.fr/H6N/tools/raw/branch/main/libs/shflags)" + + DEFINE_boolean 'dryrun' false 'Enable dry-run mode' 'd' + DEFINE_string 'git_server' 'https://git.g3e.fr/' 'Git Server' 'g' + DEFINE_string 'repo_path' 'syonad/two/' 'Path of repository' 'r' + DEFINE_string 'branch' 'main/' 'Branch name' 'b' + + check_latest_script "${FLAGS_git_server}${FLAGS_repo_path}raw/branch/${FLAGS_branch}${SCRIPT_PATH}" "${0}" +} + +[[ "${BASH_SOURCE[0]}" == "${0}" ]] && (main "$@" || exit 1) +[[ "${BASH_SOURCE[0]}" == "" ]] && (main "$@" || exit 1) \ No newline at end of file From 05b3827e3cdd6c48c3a6ac02699904f833167fe6 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Thu, 18 Dec 2025 14:09:37 +0100 Subject: [PATCH 03/84] f-1: fix: fix forgeten args #1 Signed-off-by: GnomeZworc --- scripts/deploy.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 9fcd1dd..3345488 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -24,6 +24,7 @@ check_latest_script () { } main () { + echo "$@" [[ -f ./libs/shflags ]] && . ./libs/shflags || eval "$(curl --silent https://git.g3e.fr/H6N/tools/raw/branch/main/libs/shflags)" DEFINE_boolean 'dryrun' false 'Enable dry-run mode' 'd' @@ -31,6 +32,9 @@ main () { DEFINE_string 'repo_path' 'syonad/two/' 'Path of repository' 'r' DEFINE_string 'branch' 'main/' 'Branch name' 'b' + FLAGS "$@" || exit $? + eval set -- "${FLAGS_ARGV}" + check_latest_script "${FLAGS_git_server}${FLAGS_repo_path}raw/branch/${FLAGS_branch}${SCRIPT_PATH}" "${0}" } From b25ce92c66abbe878ff669f1b05de870a93fba12 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Thu, 18 Dec 2025 14:30:04 +0100 Subject: [PATCH 04/84] f-1: scripts: edit script add dry-run Signed-off-by: GnomeZworc --- scripts/deploy.sh | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 3345488..635803f 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -11,6 +11,21 @@ esac SCRIPT_PATH="scripts/deploy.sh" +exec_with_dry_run () { + if [[ ${1} -eq ${FLAGS_TRUE} ]]; then + echo "# ${2}" + else + eval "${2}" 2> /tmp/error || \ + { + echo -e "failed with following error"; + output=$(cat /tmp/error | sed -e "s/^/ error -> /g"); + echo -e "${output}"; + return 1; + } + fi + return 0 +} + check_latest_script () { REMOTE_URL="${1}" LOCAL_PATH="${2}" @@ -18,16 +33,15 @@ check_latest_script () { REMOTE=$(curl --silent "${REMOTE_URL}" | sha256sum) LOCAL=$(cat ${LOCAL_PATH} | sha256sum) - [[ "${REMOTE}" == "${LOCAL}" ]] \ - && echo "both are the same" \ - || echo "not the same" + [[ "${REMOTE}" == "${LOCAL}" ]] || return 1 + return 0 } main () { - echo "$@" [[ -f ./libs/shflags ]] && . ./libs/shflags || eval "$(curl --silent https://git.g3e.fr/H6N/tools/raw/branch/main/libs/shflags)" DEFINE_boolean 'dryrun' false 'Enable dry-run mode' 'd' + DEFINE_boolean 'up_script' true 'Upgrade script' 's' DEFINE_string 'git_server' 'https://git.g3e.fr/' 'Git Server' 'g' DEFINE_string 'repo_path' 'syonad/two/' 'Path of repository' 'r' DEFINE_string 'branch' 'main/' 'Branch name' 'b' @@ -35,7 +49,12 @@ main () { FLAGS "$@" || exit $? eval set -- "${FLAGS_ARGV}" - check_latest_script "${FLAGS_git_server}${FLAGS_repo_path}raw/branch/${FLAGS_branch}${SCRIPT_PATH}" "${0}" + SCRIPT_URL="${FLAGS_git_server}${FLAGS_repo_path}raw/branch/${FLAGS_branch}${SCRIPT_PATH}" + check_latest_script "${SCRIPT_URL}" "${0}" || ( + [[ ${FLAGS_up_script} -eq ${FLAGS_TRUE} ]] && \ + exec_with_dry_run "${FLAGS_dryrun}" "curl --silent \"${SCRIPT_URL}\" -o \"${0}\"" + exit 1 + ) } [[ "${BASH_SOURCE[0]}" == "${0}" ]] && (main "$@" || exit 1) From 6ed9d8abd924cabf6796852f01b995c6edb5ef77 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Thu, 18 Dec 2025 15:29:09 +0100 Subject: [PATCH 05/84] f-1: scripts: add download scipts #1 Signed-off-by: GnomeZworc --- scripts/deploy.sh | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 635803f..8feee6f 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -37,6 +37,33 @@ check_latest_script () { return 0 } +download_binaries () { + DRY_RUN="${1}" + TAG="${2}" + GIT_SERVER="${3}" + REPO_PATH="${4}" + + #'.[0].assets.[].browser_download_url' + [[ "${TAG}" == "" ]] && TAG=$(curl --silent "${GIT_SERVER}api/v1/repos/${REPO_PATH}releases/?limit=1" | jq -r '.[0].tag_name') + echo "${TAG}" + + BIN_PATH="/opt/two/${TAG}/bin/" + LN_PATH="/opt/two/bin/" + + exec_with_dry_run "${DRY_RUN}" "mkdir -p \"${BIN_PATH}\"" + exec_with_dry_run "${DRY_RUN}" "mkdir -p \"${LN_PATH}\"" + + curl --silent "${GIT_SERVER}api/v1/repos/${REPO_PATH}releases/tags/${TAG}" | jq -c '.assets.[]' | while read tmp + do + BINARY_NAME=$(echo "${tmp}" | jq -r '.name') + BINARY_SHORT_NAME=$(echo "${BINARY_NAME}" | cut -d_ -f 1) + BINARY_URL=$(echo "${tmp}" | jq -r '.browser_download_url') + exec_with_dry_run "${DRY_RUN}" "curl --silent '${BINARY_URL}' -o '${BIN_PATH}${BINARY_NAME}'" + exec_with_dry_run "${DRY_RUN}" "rm -f '${LN_PATH}${BINARY_SHORT_NAME}'" + exec_with_dry_run "${DRY_RUN}" "ln -s '${BIN_PATH}${BINARY_NAME}' '${LN_PATH}${BINARY_SHORT_NAME}'" + done +} + main () { [[ -f ./libs/shflags ]] && . ./libs/shflags || eval "$(curl --silent https://git.g3e.fr/H6N/tools/raw/branch/main/libs/shflags)" @@ -45,6 +72,7 @@ main () { DEFINE_string 'git_server' 'https://git.g3e.fr/' 'Git Server' 'g' DEFINE_string 'repo_path' 'syonad/two/' 'Path of repository' 'r' DEFINE_string 'branch' 'main/' 'Branch name' 'b' + DEFINE_string 'tag' '' 'Tag name' 't' FLAGS "$@" || exit $? eval set -- "${FLAGS_ARGV}" @@ -52,9 +80,11 @@ main () { SCRIPT_URL="${FLAGS_git_server}${FLAGS_repo_path}raw/branch/${FLAGS_branch}${SCRIPT_PATH}" check_latest_script "${SCRIPT_URL}" "${0}" || ( [[ ${FLAGS_up_script} -eq ${FLAGS_TRUE} ]] && \ - exec_with_dry_run "${FLAGS_dryrun}" "curl --silent \"${SCRIPT_URL}\" -o \"${0}\"" + exec_with_dry_run "${FLAGS_dryrun}" "curl --silent '${SCRIPT_URL}' -o '${0}'" exit 1 ) + + download_binaries "${FLAGS_dryrun}" "${FLAGS_tag}" "${FLAGS_git_server}" "${FLAGS_repo_path}" } [[ "${BASH_SOURCE[0]}" == "${0}" ]] && (main "$@" || exit 1) From ec1d1e0588fb486399e58df9ed5e963bed149c2f Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Thu, 18 Dec 2025 23:04:34 +0100 Subject: [PATCH 06/84] f-1: scripts: fix jq parsing for linux Signed-off-by: GnomeZworc --- scripts/deploy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 8feee6f..ddd9795 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -53,7 +53,7 @@ download_binaries () { exec_with_dry_run "${DRY_RUN}" "mkdir -p \"${BIN_PATH}\"" exec_with_dry_run "${DRY_RUN}" "mkdir -p \"${LN_PATH}\"" - curl --silent "${GIT_SERVER}api/v1/repos/${REPO_PATH}releases/tags/${TAG}" | jq -c '.assets.[]' | while read tmp + curl --silent "${GIT_SERVER}api/v1/repos/${REPO_PATH}releases/tags/${TAG}" | jq -c '.assets[]' | while read tmp do BINARY_NAME=$(echo "${tmp}" | jq -r '.name') BINARY_SHORT_NAME=$(echo "${BINARY_NAME}" | cut -d_ -f 1) From 8d4a66bc89fa4db3a9f6797d5a4aa312643828d4 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Thu, 18 Dec 2025 23:05:26 +0100 Subject: [PATCH 07/84] f-1: scripts: add change execut for db #1 Signed-off-by: GnomeZworc --- scripts/deploy.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/deploy.sh b/scripts/deploy.sh index ddd9795..5fb0068 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -59,6 +59,7 @@ download_binaries () { BINARY_SHORT_NAME=$(echo "${BINARY_NAME}" | cut -d_ -f 1) BINARY_URL=$(echo "${tmp}" | jq -r '.browser_download_url') exec_with_dry_run "${DRY_RUN}" "curl --silent '${BINARY_URL}' -o '${BIN_PATH}${BINARY_NAME}'" + exec_with_dry_run "${DRY_RUN}" "chmod +x '${BIN_PATH}${BINARY_NAME}'" exec_with_dry_run "${DRY_RUN}" "rm -f '${LN_PATH}${BINARY_SHORT_NAME}'" exec_with_dry_run "${DRY_RUN}" "ln -s '${BIN_PATH}${BINARY_NAME}' '${LN_PATH}${BINARY_SHORT_NAME}'" done From fc6bb43616c5427c7c6289dd38cf6616139dfaff Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Thu, 18 Dec 2025 23:06:02 +0100 Subject: [PATCH 08/84] f-1: scripts: add correct echo Signed-off-by: GnomeZworc --- scripts/deploy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 5fb0068..7532b14 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -45,7 +45,7 @@ download_binaries () { #'.[0].assets.[].browser_download_url' [[ "${TAG}" == "" ]] && TAG=$(curl --silent "${GIT_SERVER}api/v1/repos/${REPO_PATH}releases/?limit=1" | jq -r '.[0].tag_name') - echo "${TAG}" + echo "Deploy ${TAG} binaries" BIN_PATH="/opt/two/${TAG}/bin/" LN_PATH="/opt/two/bin/" From 3831aa2d71cf05143c77ee8902c322b5e8aa1cde Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Thu, 18 Dec 2025 23:39:14 +0100 Subject: [PATCH 09/84] f-8: package: add go-systemd #8 Signed-off-by: GnomeZworc --- go.mod | 2 ++ go.sum | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/go.mod b/go.mod index cbc34b5..555ca1b 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.23.8 require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/coreos/go-systemd/v22 v22.6.0 // indirect github.com/dgraph-io/badger/v4 v4.8.0 // indirect github.com/dgraph-io/ristretto/v2 v2.2.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect @@ -11,6 +12,7 @@ require ( github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/google/flatbuffers v25.2.10+incompatible // indirect github.com/klauspost/compress v1.18.0 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect diff --git a/go.sum b/go.sum index 2482f6c..f61e769 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/coreos/go-systemd/v22 v22.6.0 h1:aGVa/v8B7hpb0TKl0MWoAavPDmHvobFe5R5zn0bCJWo= +github.com/coreos/go-systemd/v22 v22.6.0/go.mod h1:iG+pp635Fo7ZmV/j14KUcmEyWF+0X7Lua8rrTWzYgWU= github.com/dgraph-io/badger/v4 v4.8.0 h1:JYph1ChBijCw8SLeybvPINizbDKWZ5n/GYbz2yhN/bs= github.com/dgraph-io/badger/v4 v4.8.0/go.mod h1:U6on6e8k/RTbUWxqKR0MvugJuVmkxSNc79ap4917h4w= github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM= @@ -15,6 +17,8 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q= github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= From 6a225a616282149d432dd2a826ba74d157af2814 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Thu, 18 Dec 2025 23:39:46 +0100 Subject: [PATCH 10/84] f-8: code: add systemd lib #8 Signed-off-by: GnomeZworc --- pkg/systemd/main.go | 109 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 pkg/systemd/main.go diff --git a/pkg/systemd/main.go b/pkg/systemd/main.go new file mode 100644 index 0000000..ea5fef0 --- /dev/null +++ b/pkg/systemd/main.go @@ -0,0 +1,109 @@ +package systemd + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/coreos/go-systemd/v22/dbus" +) + +const ( + defaultTimeout = 5 * time.Second + jobMode = "replace" +) + +type Manager struct { + conn *dbus.Conn +} + +type ServiceStatus struct { + Name string + LoadState string + ActiveState string + SubState string + MainPID uint32 +} + +// New crée une connexion D-Bus systemd (scope système) +func New() (*Manager, error) { + ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) + defer cancel() + + conn, err := dbus.NewSystemConnectionContext(ctx) + if err != nil { + return nil, err + } + + return &Manager{conn: conn}, nil +} + +// Close ferme la connexion D-Bus +func (m *Manager) Close() { + if m.conn != nil { + m.conn.Close() + } +} + +// Start démarre un service systemd +func (m *Manager) Start(service string) error { + return m.job("StartUnit", service) +} + +// Stop arrête un service systemd +func (m *Manager) Stop(service string) error { + return m.job("StopUnit", service) +} + +func (m *Manager) job(method, service string) error { + ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) + defer cancel() + + ch := make(chan string, 1) + + var err error + switch method { + case "StartUnit": + _, err = m.conn.StartUnitContext(ctx, service, jobMode, ch) + case "StopUnit": + _, err = m.conn.StopUnitContext(ctx, service, jobMode, ch) + default: + return errors.New("unsupported job method") + } + + if err != nil { + return err + } + + result := <-ch + if result != "done" { + return fmt.Errorf("%s %s failed: %s", method, service, result) + } + + return nil +} + +// Status retourne l’état courant du service +func (m *Manager) Status(service string) (*ServiceStatus, error) { + ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) + defer cancel() + + props, err := m.conn.GetUnitPropertiesContext(ctx, service) + if err != nil { + return nil, err + } + + status := &ServiceStatus{ + Name: service, + LoadState: props["LoadState"].(string), + ActiveState: props["ActiveState"].(string), + SubState: props["SubState"].(string), + } + + if pid, ok := props["MainPID"].(uint32); ok { + status.MainPID = pid + } + + return status, nil +} From 72b2e9afa9bf62d759e7182f206ce561f0b622c4 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Mon, 22 Dec 2025 22:49:17 +0100 Subject: [PATCH 11/84] f-8: code: add first agent binari #8 Signed-off-by: GnomeZworc --- cmd/agent/main.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 cmd/agent/main.go diff --git a/cmd/agent/main.go b/cmd/agent/main.go new file mode 100644 index 0000000..bcb3849 --- /dev/null +++ b/cmd/agent/main.go @@ -0,0 +1,14 @@ +package main + +import ( + "fmt" + "os" +) + +func main() { + bin_name := os.Args[0] + + fmt.Printf("%s: Start process\n", bin_name) + + os.Exit(5) +} From e1f317aeb9be38d5b74bb35f5e923f27d7bb03fc Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Tue, 23 Dec 2025 23:24:14 +0100 Subject: [PATCH 12/84] f-8: code: first implement of metadata cli #8 Signed-off-by: GnomeZworc --- cmd/meta_cli/main.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 cmd/meta_cli/main.go diff --git a/cmd/meta_cli/main.go b/cmd/meta_cli/main.go new file mode 100644 index 0000000..9fec4db --- /dev/null +++ b/cmd/meta_cli/main.go @@ -0,0 +1,17 @@ +package main + +import ( + "fmt" + "os" +) + +var ( + bin_name = os.Args[0] +) + +func main() { + + fmt.Printf("Start %s conf\n", bin_name) + + os.Exit(0) +} From 1ac5a9fe4996679e32073e23db24a87b45c4289f Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Wed, 24 Dec 2025 14:04:30 +0100 Subject: [PATCH 13/84] f-8: conf: move configuration files #8 Signed-off-by: GnomeZworc --- conf/agent/config.dev.yml | 2 ++ {exemple => conf}/agent/config.exemple.yml | 0 2 files changed, 2 insertions(+) create mode 100644 conf/agent/config.dev.yml rename {exemple => conf}/agent/config.exemple.yml (100%) diff --git a/conf/agent/config.dev.yml b/conf/agent/config.dev.yml new file mode 100644 index 0000000..62cb624 --- /dev/null +++ b/conf/agent/config.dev.yml @@ -0,0 +1,2 @@ +database: + path: "./data/" \ No newline at end of file diff --git a/exemple/agent/config.exemple.yml b/conf/agent/config.exemple.yml similarity index 100% rename from exemple/agent/config.exemple.yml rename to conf/agent/config.exemple.yml From 014ae61dbbc78288abe1d31d81e7504b6bd8b413 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Wed, 24 Dec 2025 14:05:47 +0100 Subject: [PATCH 14/84] f-8: internal/conf: delete conf file existance check #8 Signed-off-by: GnomeZworc --- internal/config/agent/struct.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/internal/config/agent/struct.go b/internal/config/agent/struct.go index 12a46d3..c9537bf 100644 --- a/internal/config/agent/struct.go +++ b/internal/config/agent/struct.go @@ -17,9 +17,7 @@ func LoadConfig(path string) (*Config, error) { v.SetDefault("database.path", "/var/lib/two/data/") - if err := v.ReadInConfig(); err != nil { - return nil, err - } + v.ReadInConfig() var cfg Config if err := v.Unmarshal(&cfg); err != nil { From 2418e08ee0c9649925bcf347689a3d63ce8680ae Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Wed, 24 Dec 2025 22:02:18 +0100 Subject: [PATCH 15/84] f-8: code: add params for meta_cli #8 Signed-off-by: GnomeZworc --- cmd/meta_cli/main.go | 45 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/cmd/meta_cli/main.go b/cmd/meta_cli/main.go index 9fec4db..ce97286 100644 --- a/cmd/meta_cli/main.go +++ b/cmd/meta_cli/main.go @@ -1,17 +1,54 @@ package main import ( + "flag" "fmt" "os" + "strings" + + configuration "git.g3e.fr/syonad/two/internal/config/agent" + "git.g3e.fr/syonad/two/pkg/db/kv" + "github.com/dgraph-io/badger/v4" ) -var ( - bin_name = os.Args[0] -) +var DB *badger.DB + +func AddInDB(dbName string, line string) error { + // ID = partie avant le premier ';' + id := strings.Split(line, ";")[0] + "/bash" + key := []byte(dbName + "/" + id) + + return DB.Update(func(txn *badger.Txn) error { + return txn.Set(key, []byte(line)) + }) +} func main() { + conf_file := flag.String("conf", "/etc/two/agent.yml", "configuration file") + vm_name := flag.String("vm_name", "", "Nom de la vm") + vpc := flag.String("vpc_name", "", "vpc name") + bind_ip := flag.String("ip", "", "bind ip") + bind_port := flag.String("port", "", "bind port") + ssh_key := flag.String("key", "", "Clef ssh") + password := flag.String("pass", "", "password user") + start := flag.Bool("start", false, "start metadata server") + stop := flag.Bool("stop", false, "stop metadata server") - fmt.Printf("Start %s conf\n", bin_name) + flag.Parse() + + conf, err := configuration.LoadConfig(*conf_file) + if err != nil { + fmt.Println(err) + return + } + fmt.Print(conf) + + DB = kv.InitDB(kv.Config{ + Path: conf.Database.Path, + }) + defer DB.Close() + + fmt.Printf("conf metadata for %s\n - this key %s\n - this password %s\n", *vm_name, *ssh_key, *password) os.Exit(0) } From b43e45488e2e8339ef27af0ea34c0c32b1e9e152 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Fri, 26 Dec 2025 23:00:13 +0100 Subject: [PATCH 16/84] f-8: code: add func to control db #8 Signed-off-by: GnomeZworc --- pkg/db/kv/addInDB.go | 11 +++++++++++ pkg/db/kv/deleteInDB.go | 11 +++++++++++ 2 files changed, 22 insertions(+) create mode 100644 pkg/db/kv/addInDB.go create mode 100644 pkg/db/kv/deleteInDB.go diff --git a/pkg/db/kv/addInDB.go b/pkg/db/kv/addInDB.go new file mode 100644 index 0000000..44ea19c --- /dev/null +++ b/pkg/db/kv/addInDB.go @@ -0,0 +1,11 @@ +package kv + +import ( + "github.com/dgraph-io/badger/v4" +) + +func AddInDB(db *badger.DB, key string, value string) error { + return db.Update(func(txn *badger.Txn) error { + return txn.Set([]byte(key), []byte(value)) + }) +} diff --git a/pkg/db/kv/deleteInDB.go b/pkg/db/kv/deleteInDB.go new file mode 100644 index 0000000..fe8398e --- /dev/null +++ b/pkg/db/kv/deleteInDB.go @@ -0,0 +1,11 @@ +package kv + +import ( + "github.com/dgraph-io/badger/v4" +) + +func DeleteInDB(db *badger.DB, key string) error { + return db.Update(func(txn *badger.Txn) error { + return txn.Delete([]byte(key)) + }) +} From 55ed2c8e53ca0a8ce740abfa0f9c7bd812301bd4 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Fri, 26 Dec 2025 23:00:51 +0100 Subject: [PATCH 17/84] f-8: code: add module to load nocloud in db #8 Signed-off-by: GnomeZworc --- internal/load_db/nocloud/render.go | 50 +++++++++++++++++++ internal/load_db/nocloud/struct.go | 10 ++++ .../load_db/nocloud/templates/meta-data.tmpl | 2 + .../nocloud/templates/network-config.tmpl | 4 ++ .../load_db/nocloud/templates/user-data.tmpl | 3 ++ .../nocloud/templates/vendor-data.tmpl | 13 +++++ 6 files changed, 82 insertions(+) create mode 100644 internal/load_db/nocloud/render.go create mode 100644 internal/load_db/nocloud/struct.go create mode 100644 internal/load_db/nocloud/templates/meta-data.tmpl create mode 100644 internal/load_db/nocloud/templates/network-config.tmpl create mode 100644 internal/load_db/nocloud/templates/user-data.tmpl create mode 100644 internal/load_db/nocloud/templates/vendor-data.tmpl diff --git a/internal/load_db/nocloud/render.go b/internal/load_db/nocloud/render.go new file mode 100644 index 0000000..ad7eee1 --- /dev/null +++ b/internal/load_db/nocloud/render.go @@ -0,0 +1,50 @@ +package nocloud + +import ( + "bytes" + "embed" + "text/template" + + "git.g3e.fr/syonad/two/pkg/db/kv" + "github.com/dgraph-io/badger/v4" +) + +//go:embed templates/*.tmpl +var templateFS embed.FS + +func renderConfig(path string, cfg Config) (string, error) { + tpl, err := template.ParseFS(templateFS, path) + if err != nil { + return "", err + } + + var buf bytes.Buffer + if err := tpl.Execute(&buf, cfg); err != nil { + return "", err + } + + return buf.String(), nil +} + +var DB *badger.DB + +func LoadNcCloudInDB(config Config, db *badger.DB) { + meta_data, _ := renderConfig("templates/meta-data.tmpl", config) + user_data, _ := renderConfig("templates/user-data.tmpl", config) + network_config, _ := renderConfig("templates/network-config.tmpl", config) + vendor_data, _ := renderConfig("templates/vendor-data.tmpl", config) + + DB = db + + kv.AddInDB(DB, "metadata/"+config.Name+"/meta-data", meta_data) + kv.AddInDB(DB, "metadata/"+config.Name+"/user-data", user_data) + kv.AddInDB(DB, "metadata/"+config.Name+"/network-config", network_config) + kv.AddInDB(DB, "metadata/"+config.Name+"/vendor-data", vendor_data) + kv.AddInDB(DB, "metadata/"+config.Name+"/vpc", config.VpcName) + kv.AddInDB(DB, "metadata/"+config.Name+"/bind_ip", config.BindIP) + kv.AddInDB(DB, "metadata/"+config.Name+"/bind_port", config.BindPort) +} + +func UnLoadNoCloudInDB(vm_name string, db *badger.DB) { + kv.DeleteInDB(DB, "metadata/"+vm_name) +} diff --git a/internal/load_db/nocloud/struct.go b/internal/load_db/nocloud/struct.go new file mode 100644 index 0000000..4134000 --- /dev/null +++ b/internal/load_db/nocloud/struct.go @@ -0,0 +1,10 @@ +package nocloud + +type Config struct { + VpcName string + BindIP string + BindPort string + Name string + Password string + SSHKEY string +} diff --git a/internal/load_db/nocloud/templates/meta-data.tmpl b/internal/load_db/nocloud/templates/meta-data.tmpl new file mode 100644 index 0000000..ff876bb --- /dev/null +++ b/internal/load_db/nocloud/templates/meta-data.tmpl @@ -0,0 +1,2 @@ +instance-id: {{ .Name }} +local-hostname: {{ .Name }} diff --git a/internal/load_db/nocloud/templates/network-config.tmpl b/internal/load_db/nocloud/templates/network-config.tmpl new file mode 100644 index 0000000..0f8d052 --- /dev/null +++ b/internal/load_db/nocloud/templates/network-config.tmpl @@ -0,0 +1,4 @@ +version: 2 +ethernets: + eth0: + dhcp4: true diff --git a/internal/load_db/nocloud/templates/user-data.tmpl b/internal/load_db/nocloud/templates/user-data.tmpl new file mode 100644 index 0000000..84195ab --- /dev/null +++ b/internal/load_db/nocloud/templates/user-data.tmpl @@ -0,0 +1,3 @@ +#!/bin/sh + +passwd -d root diff --git a/internal/load_db/nocloud/templates/vendor-data.tmpl b/internal/load_db/nocloud/templates/vendor-data.tmpl new file mode 100644 index 0000000..148d6db --- /dev/null +++ b/internal/load_db/nocloud/templates/vendor-data.tmpl @@ -0,0 +1,13 @@ +#cloud-config +users: + - name: syonad + lock_passwd: false + gecos: alpine Cloud User + groups: [adm, wheel] + doas: + - permit nopass syonad + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + shell: /bin/ash + passwd: "{{ .Password }}" + ssh_authorized_keys: + - "{{ .SSHKEY }}" \ No newline at end of file From 1ac1e6186487d4223320bfb420cdbc60741ca58f Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Fri, 26 Dec 2025 23:02:14 +0100 Subject: [PATCH 18/84] f-8: code: move binary name #8 Signed-off-by: GnomeZworc --- cmd/agent/main.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cmd/agent/main.go b/cmd/agent/main.go index bcb3849..0f82e93 100644 --- a/cmd/agent/main.go +++ b/cmd/agent/main.go @@ -5,10 +5,13 @@ import ( "os" ) +var ( + bin_name = os.Args[0] +) + func main() { - bin_name := os.Args[0] fmt.Printf("%s: Start process\n", bin_name) - os.Exit(5) + os.Exit(0) } From df95d8b4a79fceaa0b790df081afda5b1850e2ec Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Fri, 26 Dec 2025 23:40:03 +0100 Subject: [PATCH 19/84] f-8: code: use flags in db binary #8 Signed-off-by: GnomeZworc --- cmd/db/main.go | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/cmd/db/main.go b/cmd/db/main.go index 0c6c1ba..205d8bf 100644 --- a/cmd/db/main.go +++ b/cmd/db/main.go @@ -1,6 +1,7 @@ package main import ( + "flag" "fmt" "os" "strings" @@ -125,7 +126,13 @@ func printDB() { } func main() { - conf, err := configuration.LoadConfig("/etc/two/agent.yml") + conf_file := flag.String("conf", "/etc/two/agent.yml", "configuration file") + + flag.Parse() + + args := flag.Args() + + conf, err := configuration.LoadConfig(*conf_file) if err != nil { fmt.Println(err) return @@ -136,53 +143,53 @@ func main() { }) defer DB.Close() - if len(os.Args) < 2 { + if len(args) < 1 { fmt.Println("Usage: db [args...]") return } - cmd := os.Args[1] + cmd := args[0] switch cmd { case "check_in_db": - if len(os.Args) != 4 { + if len(args) != 3 { fmt.Println("Usage: check_in_db ") os.Exit(1) } - ret := CheckInDB(os.Args[2], os.Args[3]) + ret := CheckInDB(args[1], args[2]) os.Exit(ret) case "add_in_db": - if len(os.Args) < 4 { + if len(args) < 3 { fmt.Println("Usage: add_in_db ") os.Exit(1) } - line := strings.Join(os.Args[3:], ";") - if err := AddInDB(os.Args[2], line); err != nil { + line := strings.Join(args[2:], ";") + if err := AddInDB(args[1], line); err != nil { fmt.Println("Error:", err) os.Exit(1) } case "delete_in_db": - if len(os.Args) != 4 { + if len(args) != 3 { fmt.Println("Usage: delete_in_db ") os.Exit(1) } - if err := DeleteInDB(os.Args[2], os.Args[3]); err != nil { + if err := DeleteInDB(args[1], args[2]); err != nil { fmt.Println("Error:", err) os.Exit(1) } case "count_in_db": - if len(os.Args) != 4 { + if len(args) != 3 { fmt.Println("Usage: count_in_db ") os.Exit(1) } - count := CountInDB(os.Args[2], os.Args[3]) + count := CountInDB(args[1], args[2]) fmt.Println(count) case "get_from_db": - if len(os.Args) != 4 { + if len(args) != 3 { fmt.Println("Usage: get_from_db ") os.Exit(1) } - line, _ := GetFromDB(os.Args[2], os.Args[3]) + line, _ := GetFromDB(args[1], args[2]) fmt.Println(line) case "print": printDB() From b2d1922eaa7b809034e50cbdb5ef9ae8bc9a3af6 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Fri, 26 Dec 2025 23:42:24 +0100 Subject: [PATCH 20/84] f-8: pkg: make delete in db recursif #8 Signed-off-by: GnomeZworc --- pkg/db/kv/deleteInDB.go | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/pkg/db/kv/deleteInDB.go b/pkg/db/kv/deleteInDB.go index fe8398e..36d8669 100644 --- a/pkg/db/kv/deleteInDB.go +++ b/pkg/db/kv/deleteInDB.go @@ -1,11 +1,44 @@ package kv import ( + "fmt" + "log" + "github.com/dgraph-io/badger/v4" ) -func DeleteInDB(db *badger.DB, key string) error { +func deleteKey(db *badger.DB, key string) error { return db.Update(func(txn *badger.Txn) error { return txn.Delete([]byte(key)) }) } + +func DeleteInDB(db *badger.DB, key string) error { + + prefix := []byte(key + "/") + + err := db.View(func(txn *badger.Txn) error { + opts := badger.DefaultIteratorOptions + opts.PrefetchValues = false + + it := txn.NewIterator(opts) + defer it.Close() + + for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() { + item := it.Item() + key := item.Key() + + k := append([]byte{}, key...) + fmt.Println(string(k)) + if err := deleteKey(db, string(k)); err != nil { + return err + } + } + return nil + }) + if err != nil { + log.Fatal(err) + } + + return deleteKey(db, key) +} From 38f2ed1b83190cb9b7434659d17968d1d25684c4 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Fri, 26 Dec 2025 23:43:29 +0100 Subject: [PATCH 21/84] f-8: code: use recursive delete #8 Signed-off-by: GnomeZworc --- cmd/db/main.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cmd/db/main.go b/cmd/db/main.go index 205d8bf..765cad0 100644 --- a/cmd/db/main.go +++ b/cmd/db/main.go @@ -60,9 +60,7 @@ func AddInDB(dbName string, line string) error { func DeleteInDB(dbName, id string) error { key := []byte(dbName + "/" + id + "/bash") - return DB.Update(func(txn *badger.Txn) error { - return txn.Delete(key) - }) + return kv.DeleteInDB(DB, string(key)) } func CountInDB(dbName, id string) int { From 97132550b0b000e53cee7801152c1f84d04099f2 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Fri, 26 Dec 2025 23:46:18 +0100 Subject: [PATCH 22/84] f-8: code: move Load in DB to internal lib #8 Signed-off-by: GnomeZworc --- cmd/meta_cli/main.go | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/cmd/meta_cli/main.go b/cmd/meta_cli/main.go index ce97286..971c370 100644 --- a/cmd/meta_cli/main.go +++ b/cmd/meta_cli/main.go @@ -3,26 +3,12 @@ package main import ( "flag" "fmt" - "os" - "strings" configuration "git.g3e.fr/syonad/two/internal/config/agent" + "git.g3e.fr/syonad/two/internal/load_db/nocloud" "git.g3e.fr/syonad/two/pkg/db/kv" - "github.com/dgraph-io/badger/v4" ) -var DB *badger.DB - -func AddInDB(dbName string, line string) error { - // ID = partie avant le premier ';' - id := strings.Split(line, ";")[0] + "/bash" - key := []byte(dbName + "/" + id) - - return DB.Update(func(txn *badger.Txn) error { - return txn.Set(key, []byte(line)) - }) -} - func main() { conf_file := flag.String("conf", "/etc/two/agent.yml", "configuration file") vm_name := flag.String("vm_name", "", "Nom de la vm") @@ -43,12 +29,21 @@ func main() { } fmt.Print(conf) - DB = kv.InitDB(kv.Config{ + db := kv.InitDB(kv.Config{ Path: conf.Database.Path, }) - defer DB.Close() + defer db.Close() - fmt.Printf("conf metadata for %s\n - this key %s\n - this password %s\n", *vm_name, *ssh_key, *password) - - os.Exit(0) + if *start { + nocloud.LoadNcCloudInDB(nocloud.Config{ + VpcName: *vpc, + Name: *vm_name, + BindIP: *bind_ip, + BindPort: *bind_port, + Password: *password, + SSHKEY: *ssh_key, + }, db) + } else if *stop { + nocloud.UnLoadNoCloudInDB(*vm_name, db) + } } From c2664e94fa44a822c8e50dc2b5ccf611eb1e2239 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Sun, 28 Dec 2025 14:20:12 +0100 Subject: [PATCH 23/84] f-8: clean: use kv func for db bin #8 Signed-off-by: GnomeZworc --- cmd/db/main.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cmd/db/main.go b/cmd/db/main.go index 765cad0..8b8d25e 100644 --- a/cmd/db/main.go +++ b/cmd/db/main.go @@ -52,9 +52,7 @@ func AddInDB(dbName string, line string) error { id := strings.Split(line, ";")[0] + "/bash" key := []byte(dbName + "/" + id) - return DB.Update(func(txn *badger.Txn) error { - return txn.Set(key, []byte(line)) - }) + return kv.AddInDB(DB, string(key), line) } func DeleteInDB(dbName, id string) error { From 348eb4aee32b127480087826b5f55ce0c2bb4cbf Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Sun, 28 Dec 2025 14:57:36 +0100 Subject: [PATCH 24/84] f-8: code: implemete netns first code #8 Signed-off-by: GnomeZworc --- go.mod | 6 ++++-- go.sum | 2 ++ internal/netns/enter.go | 5 +++++ internal/netns/enter_linux.go | 26 ++++++++++++++++++++++++++ internal/netns/enter_other.go | 8 ++++++++ 5 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 internal/netns/enter.go create mode 100644 internal/netns/enter_linux.go create mode 100644 internal/netns/enter_other.go diff --git a/go.mod b/go.mod index 555ca1b..26bd137 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module git.g3e.fr/syonad/two -go 1.23.8 +go 1.24.0 + +toolchain go1.24.11 require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect @@ -29,7 +31,7 @@ require ( go.opentelemetry.io/otel/trace v1.37.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/net v0.41.0 // indirect - golang.org/x/sys v0.34.0 // indirect + golang.org/x/sys v0.39.0 // indirect golang.org/x/text v0.28.0 // indirect google.golang.org/protobuf v1.36.6 // indirect ) diff --git a/go.sum b/go.sum index f61e769..0d19489 100644 --- a/go.sum +++ b/go.sum @@ -53,6 +53,8 @@ golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= diff --git a/internal/netns/enter.go b/internal/netns/enter.go new file mode 100644 index 0000000..68ea372 --- /dev/null +++ b/internal/netns/enter.go @@ -0,0 +1,5 @@ +package netns + +func Enter(name string) error { + return enter(name) +} diff --git a/internal/netns/enter_linux.go b/internal/netns/enter_linux.go new file mode 100644 index 0000000..04e116a --- /dev/null +++ b/internal/netns/enter_linux.go @@ -0,0 +1,26 @@ +//go:build linux + +package netns + +import ( + "fmt" + "os" + "runtime" + + "golang.org/x/sys/unix" +) + +func enter(name string) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + path := fmt.Sprintf("/var/run/netns/%s", name) + + f, err := os.Open(path) + if err != nil { + return err + } + defer f.Close() + + return unix.Setns(int(f.Fd()), unix.CLONE_NEWNET) +} diff --git a/internal/netns/enter_other.go b/internal/netns/enter_other.go new file mode 100644 index 0000000..2691ff2 --- /dev/null +++ b/internal/netns/enter_other.go @@ -0,0 +1,8 @@ +//go:build !linux + +package netns + +func enter(name string) error { + // Ignoré hors Linux + return nil +} From 2c6c1ff2dfb1f0c0eaf590e2fff2452db6285c7b Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Sun, 28 Dec 2025 14:58:09 +0100 Subject: [PATCH 25/84] f-8: code: use local usage of netns #8 Signed-off-by: GnomeZworc --- internal/metadata/server.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/internal/metadata/server.go b/internal/metadata/server.go index 336f754..2e43a1a 100644 --- a/internal/metadata/server.go +++ b/internal/metadata/server.go @@ -9,14 +9,17 @@ import ( "net" "net/http" "time" + + "git.g3e.fr/syonad/two/internal/netns" ) var data NoCloudData var ( - iface = flag.String("interface", "0.0.0.0", "Interface IP à écouter") - port = flag.Int("port", 8080, "Port à utiliser") - file = flag.String("file", "", "Fichier JSON contenant les données NoCloud") + iface = flag.String("interface", "0.0.0.0", "Interface IP à écouter") + port = flag.Int("port", 8080, "Port à utiliser") + file = flag.String("file", "", "Fichier JSON contenant les données NoCloud") + netns_name = flag.String("netns", "", "Network namespace à utiliser") ) func getIP(r *http.Request) string { @@ -54,6 +57,12 @@ func rootHandler(w http.ResponseWriter, r *http.Request) { func StartServer() { flag.Parse() + if *netns_name != "" { + if err := netns.Enter(*netns_name); err != nil { + log.Fatalf("Impossible d'entrer dans le netns: %v", err) + } + } + if *file == "" { log.Fatal("Vous devez spécifier un fichier via --file") } From e3583e70215ea8c7624d42121c8cef555c046446 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Sun, 28 Dec 2025 15:01:37 +0100 Subject: [PATCH 26/84] f-8: import local info for start/deploy metadata #8 Signed-off-by: GnomeZworc --- systemd/metadata_service/metadata@.service | 10 ++++++++++ systemd/metadata_start/run-metadata-in-netns.sh | 17 +++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 systemd/metadata_service/metadata@.service create mode 100644 systemd/metadata_start/run-metadata-in-netns.sh diff --git a/systemd/metadata_service/metadata@.service b/systemd/metadata_service/metadata@.service new file mode 100644 index 0000000..54cf08a --- /dev/null +++ b/systemd/metadata_service/metadata@.service @@ -0,0 +1,10 @@ +[Unit] +Description=metadata in netns %i +After=network.target + +[Service] +Type=simple +ExecStart=/usr/local/bin/run-metadata-in-netns.sh %i + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/systemd/metadata_start/run-metadata-in-netns.sh b/systemd/metadata_start/run-metadata-in-netns.sh new file mode 100644 index 0000000..612dd18 --- /dev/null +++ b/systemd/metadata_start/run-metadata-in-netns.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -e + +# Expects one argument: netns_bridge (e.g. vpc-00003_br-00002 or vpc1_br0) +arg="$1" +NETNS="${arg%%_*}" +ip_port="${arg#*_}" +IP="${ip_port%%-*}" +PORT="${ip_port#*-}" + +echo "start metadata ${NETNS} " + +exec ip netns exec "${NETNS}" \ + /usr/bin/metadata \ + -file "/opt/metadata/${arg}.conf" \ + -interface "${IP}" \ + -port "${PORT}" \ No newline at end of file From 7b43cecc577330db6628eb9babe352ccb9c91b14 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Mon, 29 Dec 2025 23:35:24 +0100 Subject: [PATCH 27/84] f-8: ci: add two binaries #8 Signed-off-by: GnomeZworc --- .forgejo/workflows/prerelease.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/prerelease.yml b/.forgejo/workflows/prerelease.yml index fcb74c6..c6ea322 100644 --- a/.forgejo/workflows/prerelease.yml +++ b/.forgejo/workflows/prerelease.yml @@ -32,7 +32,7 @@ jobs: matrix: goos: [linux] goarch: [amd64] - binaries: [db, metadata] + binaries: [db, metadata, meta_cli, agent] uses: ./.forgejo/workflows/build.yml with: tag: ${{ needs.set-release-target.outputs.release_cible }} From fc624e7e961cf283b604937dfc9ca7f8398cc2b2 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Tue, 30 Dec 2025 21:27:35 +0100 Subject: [PATCH 28/84] f-8: code: move flags from pkg to binari #8 Signed-off-by: GnomeZworc --- cmd/metadata/main.go | 23 ++++++++++++++++++++++- internal/metadata/server.go | 24 +++++------------------- internal/metadata/struct.go | 7 +++++++ 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/cmd/metadata/main.go b/cmd/metadata/main.go index 54bae88..dc5bf29 100644 --- a/cmd/metadata/main.go +++ b/cmd/metadata/main.go @@ -1,9 +1,30 @@ package main import ( + "flag" + "log" + "git.g3e.fr/syonad/two/internal/metadata" ) +var ( + iface = flag.String("interface", "0.0.0.0", "Interface IP à écouter") + port = flag.Int("port", 8080, "Port à utiliser") + file = flag.String("file", "", "Fichier JSON contenant les données NoCloud") + netns_name = flag.String("netns", "", "Network namespace à utiliser") +) + func main() { - metadata.StartServer() + flag.Parse() + + if *file == "" { + log.Fatal("Vous devez spécifier un fichier via --file") + } + + metadata.StartServer(metadata.Config{ + Netns: *netns_name, + File: *file, + Iface: *iface, + Port: *port, + }) } diff --git a/internal/metadata/server.go b/internal/metadata/server.go index 2e43a1a..1cb9559 100644 --- a/internal/metadata/server.go +++ b/internal/metadata/server.go @@ -2,7 +2,6 @@ package metadata import ( "encoding/json" - "flag" "fmt" "io/ioutil" "log" @@ -15,13 +14,6 @@ import ( var data NoCloudData -var ( - iface = flag.String("interface", "0.0.0.0", "Interface IP à écouter") - port = flag.Int("port", 8080, "Port à utiliser") - file = flag.String("file", "", "Fichier JSON contenant les données NoCloud") - netns_name = flag.String("netns", "", "Network namespace à utiliser") -) - func getIP(r *http.Request) string { ip, _, err := net.SplitHostPort(r.RemoteAddr) if err != nil { @@ -54,20 +46,14 @@ func rootHandler(w http.ResponseWriter, r *http.Request) { } } -func StartServer() { - flag.Parse() - - if *netns_name != "" { - if err := netns.Enter(*netns_name); err != nil { +func StartServer(config Config) { + if config.Netns != "" { + if err := netns.Enter(config.Netns); err != nil { log.Fatalf("Impossible d'entrer dans le netns: %v", err) } } - if *file == "" { - log.Fatal("Vous devez spécifier un fichier via --file") - } - - raw, err := ioutil.ReadFile(*file) + raw, err := ioutil.ReadFile(config.File) if err != nil { log.Fatalf("Erreur de lecture du fichier: %v", err) } @@ -78,7 +64,7 @@ func StartServer() { http.HandleFunc("/", rootHandler) - address := fmt.Sprintf("%s:%d", *iface, *port) + address := fmt.Sprintf("%s:%d", config.Iface, config.Port) log.Printf("Serveur NoCloud démarré sur http://%s/", address) log.Fatal(http.ListenAndServe(address, nil)) } diff --git a/internal/metadata/struct.go b/internal/metadata/struct.go index f1edf18..bbf4bd7 100644 --- a/internal/metadata/struct.go +++ b/internal/metadata/struct.go @@ -6,3 +6,10 @@ type NoCloudData struct { NetworkConfig string `json:"network-config"` VendorData string `json:"vendor-data"` } + +type Config struct { + Netns string + File string + Iface string + Port int +} From d2bc7e5b7a131ba49fa3d5d4caa354625e1eb770 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Tue, 30 Dec 2025 22:31:19 +0100 Subject: [PATCH 29/84] f-8: code: use db for database config #8 Signed-off-by: GnomeZworc --- cmd/metadata/main.go | 19 +++++------ internal/metadata/server.go | 63 ++++++++++++++++++++++++++++--------- internal/metadata/struct.go | 21 ++++++++----- pkg/db/kv/getInDB.go | 22 +++++++++++++ 4 files changed, 92 insertions(+), 33 deletions(-) create mode 100644 pkg/db/kv/getInDB.go diff --git a/cmd/metadata/main.go b/cmd/metadata/main.go index dc5bf29..4813ed5 100644 --- a/cmd/metadata/main.go +++ b/cmd/metadata/main.go @@ -2,29 +2,26 @@ package main import ( "flag" - "log" "git.g3e.fr/syonad/two/internal/metadata" ) var ( iface = flag.String("interface", "0.0.0.0", "Interface IP à écouter") - port = flag.Int("port", 8080, "Port à utiliser") - file = flag.String("file", "", "Fichier JSON contenant les données NoCloud") + port = flag.Int("port", 0, "Port à utiliser") netns_name = flag.String("netns", "", "Network namespace à utiliser") + conf_file = flag.String("conf", "/etc/two/agent.yml", "configuration file") + vm_name = flag.String("vm", "", "Name of the vm") ) func main() { flag.Parse() - if *file == "" { - log.Fatal("Vous devez spécifier un fichier via --file") - } - metadata.StartServer(metadata.Config{ - Netns: *netns_name, - File: *file, - Iface: *iface, - Port: *port, + Netns: *netns_name, + Iface: *iface, + Port: *port, + ConfFile: *conf_file, + VmName: *vm_name, }) } diff --git a/internal/metadata/server.go b/internal/metadata/server.go index 1cb9559..831aa80 100644 --- a/internal/metadata/server.go +++ b/internal/metadata/server.go @@ -1,15 +1,15 @@ package metadata import ( - "encoding/json" "fmt" - "io/ioutil" "log" "net" "net/http" + "strconv" "time" "git.g3e.fr/syonad/two/internal/netns" + "git.g3e.fr/syonad/two/pkg/db/kv" ) var data NoCloudData @@ -22,6 +22,48 @@ func getIP(r *http.Request) string { return ip } +func getFromDB(config Config) NoCloudData { + var netns_name string + var port int + var iface string + + db := kv.InitDB(kv.Config{Path: config.ConfFile}) + + metadata, _ := kv.GetFromDB(db, "metadata/"+config.VmName+"/meta-data") + userdata, _ := kv.GetFromDB(db, "metadata/"+config.VmName+"/user-data") + networkconfig, _ := kv.GetFromDB(db, "metadata/"+config.VmName+"/network-config") + vendordata, _ := kv.GetFromDB(db, "metadata/"+config.VmName+"/vendor-data") + + if config.Netns == "" { + netns_name, _ = kv.GetFromDB(db, "metadata/"+config.VmName+"/vpc") + } else { + netns_name = config.Netns + } + + if config.Iface == "" { + iface, _ = kv.GetFromDB(db, "metadata/"+config.VmName+"/bind_ip") + } else { + iface = config.Iface + } + + if config.Port == 0 { + sport, _ := kv.GetFromDB(db, "metadata/"+config.VmName+"/bind_port") + port, _ = strconv.Atoi(sport) + } else { + port = config.Port + } + + return NoCloudData{ + MetaData: metadata, + UserData: userdata, + NetworkConfig: networkconfig, + VendorData: vendordata, + NetNs: netns_name, + Iface: iface, + Port: port, + } +} + func rootHandler(w http.ResponseWriter, r *http.Request) { ip := getIP(r) path := r.URL.Path @@ -47,24 +89,17 @@ func rootHandler(w http.ResponseWriter, r *http.Request) { } func StartServer(config Config) { - if config.Netns != "" { - if err := netns.Enter(config.Netns); err != nil { + data = getFromDB(config) + + if data.NetNs != "" { + if err := netns.Enter(data.NetNs); err != nil { log.Fatalf("Impossible d'entrer dans le netns: %v", err) } } - raw, err := ioutil.ReadFile(config.File) - if err != nil { - log.Fatalf("Erreur de lecture du fichier: %v", err) - } - - if err := json.Unmarshal(raw, &data); err != nil { - log.Fatalf("Erreur de parsing JSON: %v", err) - } - http.HandleFunc("/", rootHandler) - address := fmt.Sprintf("%s:%d", config.Iface, config.Port) + address := fmt.Sprintf("%s:%d", data.Iface, data.Port) log.Printf("Serveur NoCloud démarré sur http://%s/", address) log.Fatal(http.ListenAndServe(address, nil)) } diff --git a/internal/metadata/struct.go b/internal/metadata/struct.go index bbf4bd7..9e95341 100644 --- a/internal/metadata/struct.go +++ b/internal/metadata/struct.go @@ -1,15 +1,20 @@ package metadata type NoCloudData struct { - MetaData string `json:"meta-data"` - UserData string `json:"user-data"` - NetworkConfig string `json:"network-config"` - VendorData string `json:"vendor-data"` + MetaData string + UserData string + NetworkConfig string + VendorData string + NetNs string + Iface string + Port int } type Config struct { - Netns string - File string - Iface string - Port int + Netns string + File string + Iface string + Port int + ConfFile string + VmName string } diff --git a/pkg/db/kv/getInDB.go b/pkg/db/kv/getInDB.go new file mode 100644 index 0000000..bfe7590 --- /dev/null +++ b/pkg/db/kv/getInDB.go @@ -0,0 +1,22 @@ +package kv + +import ( + "github.com/dgraph-io/badger/v4" +) + +func GetFromDB(db *badger.DB, key string) (string, error) { + var result string + + err := db.View(func(txn *badger.Txn) error { + item, err := txn.Get([]byte(key)) + if err != nil { + return err + } + return item.Value(func(val []byte) error { + result = string(val) + return nil + }) + }) + + return result, err +} From f3898b288dfba0b70861097dfcc5ae5b20a4c5ea Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Thu, 1 Jan 2026 23:19:25 +0100 Subject: [PATCH 30/84] f-8: code: set correct config path and data path #8 Signed-off-by: GnomeZworc --- internal/metadata/server.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/internal/metadata/server.go b/internal/metadata/server.go index 831aa80..e44edaf 100644 --- a/internal/metadata/server.go +++ b/internal/metadata/server.go @@ -8,6 +8,7 @@ import ( "strconv" "time" + configuration "git.g3e.fr/syonad/two/internal/config/agent" "git.g3e.fr/syonad/two/internal/netns" "git.g3e.fr/syonad/two/pkg/db/kv" ) @@ -27,7 +28,9 @@ func getFromDB(config Config) NoCloudData { var port int var iface string - db := kv.InitDB(kv.Config{Path: config.ConfFile}) + conf_db, _ := configuration.LoadConfig(config.ConfFile) + + db := kv.InitDB(kv.Config{Path: conf_db.Database.Path}) metadata, _ := kv.GetFromDB(db, "metadata/"+config.VmName+"/meta-data") userdata, _ := kv.GetFromDB(db, "metadata/"+config.VmName+"/user-data") From 7dbb508058f34037e50f314ef5739ccfd0f1a9e2 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Thu, 1 Jan 2026 23:24:11 +0100 Subject: [PATCH 31/84] f-8: fix: close db for metadata Signed-off-by: GnomeZworc --- internal/metadata/server.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/metadata/server.go b/internal/metadata/server.go index e44edaf..a72a52e 100644 --- a/internal/metadata/server.go +++ b/internal/metadata/server.go @@ -31,6 +31,7 @@ func getFromDB(config Config) NoCloudData { conf_db, _ := configuration.LoadConfig(config.ConfFile) db := kv.InitDB(kv.Config{Path: conf_db.Database.Path}) + defer db.Close() metadata, _ := kv.GetFromDB(db, "metadata/"+config.VmName+"/meta-data") userdata, _ := kv.GetFromDB(db, "metadata/"+config.VmName+"/user-data") From 87879048338f86db1bf9035abeb1d279fec133dc Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Fri, 2 Jan 2026 22:51:15 +0100 Subject: [PATCH 32/84] f-8: code: add readonly feature in kv db #8 Signed-off-by: GnomeZworc --- pkg/db/kv/init.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/db/kv/init.go b/pkg/db/kv/init.go index 18148df..cc09cb0 100644 --- a/pkg/db/kv/init.go +++ b/pkg/db/kv/init.go @@ -4,8 +4,9 @@ import ( "github.com/dgraph-io/badger/v4" ) -func InitDB(conf Config) *badger.DB { - opts := badger.DefaultOptions(conf.Path) +func InitDB(conf Config, readonly bool) *badger.DB { + opts := badger.DefaultOptions(conf.Path). + WithReadOnly(readonly) opts.Logger = nil opts.ValueLogFileSize = 10 << 20 // 10 Mo par fichier vlog opts.NumMemtables = 1 From de7a678640394042ae0dfea4499cac777720ad79 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Fri, 2 Jan 2026 22:51:53 +0100 Subject: [PATCH 33/84] f-8: fix: implemente readonly usage #8 Signed-off-by: GnomeZworc --- cmd/db/main.go | 2 +- cmd/meta_cli/main.go | 2 +- internal/metadata/server.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/db/main.go b/cmd/db/main.go index 8b8d25e..1166f44 100644 --- a/cmd/db/main.go +++ b/cmd/db/main.go @@ -136,7 +136,7 @@ func main() { DB = kv.InitDB(kv.Config{ Path: conf.Database.Path, - }) + }, false) defer DB.Close() if len(args) < 1 { diff --git a/cmd/meta_cli/main.go b/cmd/meta_cli/main.go index 971c370..a357991 100644 --- a/cmd/meta_cli/main.go +++ b/cmd/meta_cli/main.go @@ -31,7 +31,7 @@ func main() { db := kv.InitDB(kv.Config{ Path: conf.Database.Path, - }) + }, false) defer db.Close() if *start { diff --git a/internal/metadata/server.go b/internal/metadata/server.go index a72a52e..3a63cdb 100644 --- a/internal/metadata/server.go +++ b/internal/metadata/server.go @@ -30,7 +30,7 @@ func getFromDB(config Config) NoCloudData { conf_db, _ := configuration.LoadConfig(config.ConfFile) - db := kv.InitDB(kv.Config{Path: conf_db.Database.Path}) + db := kv.InitDB(kv.Config{Path: conf_db.Database.Path}, true) defer db.Close() metadata, _ := kv.GetFromDB(db, "metadata/"+config.VmName+"/meta-data") From 93a2bc6d330c42d7abef3fdb130f88ac57bf4505 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Sun, 4 Jan 2026 17:42:26 +0100 Subject: [PATCH 34/84] f-8: systemd: change systemd file #8 Signed-off-by: GnomeZworc --- .../{metadata_service => }/metadata@.service | 2 +- systemd/metadata_start/run-metadata-in-netns.sh | 17 ----------------- 2 files changed, 1 insertion(+), 18 deletions(-) rename systemd/{metadata_service => }/metadata@.service (69%) delete mode 100644 systemd/metadata_start/run-metadata-in-netns.sh diff --git a/systemd/metadata_service/metadata@.service b/systemd/metadata@.service similarity index 69% rename from systemd/metadata_service/metadata@.service rename to systemd/metadata@.service index 54cf08a..626b97c 100644 --- a/systemd/metadata_service/metadata@.service +++ b/systemd/metadata@.service @@ -4,7 +4,7 @@ After=network.target [Service] Type=simple -ExecStart=/usr/local/bin/run-metadata-in-netns.sh %i +ExecStart=/opt/two/bin/metadata --vm %i [Install] WantedBy=multi-user.target \ No newline at end of file diff --git a/systemd/metadata_start/run-metadata-in-netns.sh b/systemd/metadata_start/run-metadata-in-netns.sh deleted file mode 100644 index 612dd18..0000000 --- a/systemd/metadata_start/run-metadata-in-netns.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -set -e - -# Expects one argument: netns_bridge (e.g. vpc-00003_br-00002 or vpc1_br0) -arg="$1" -NETNS="${arg%%_*}" -ip_port="${arg#*_}" -IP="${ip_port%%-*}" -PORT="${ip_port#*-}" - -echo "start metadata ${NETNS} " - -exec ip netns exec "${NETNS}" \ - /usr/bin/metadata \ - -file "/opt/metadata/${arg}.conf" \ - -interface "${IP}" \ - -port "${PORT}" \ No newline at end of file From 989264681426a5fd472a5c39f8d740c9ddaeae94 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Sun, 4 Jan 2026 17:43:51 +0100 Subject: [PATCH 35/84] f-8: code: start/stop metadata service Signed-off-by: GnomeZworc --- cmd/meta_cli/main.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmd/meta_cli/main.go b/cmd/meta_cli/main.go index a357991..9782b5f 100644 --- a/cmd/meta_cli/main.go +++ b/cmd/meta_cli/main.go @@ -7,6 +7,7 @@ import ( configuration "git.g3e.fr/syonad/two/internal/config/agent" "git.g3e.fr/syonad/two/internal/load_db/nocloud" "git.g3e.fr/syonad/two/pkg/db/kv" + "git.g3e.fr/syonad/two/pkg/systemd" ) func main() { @@ -22,6 +23,8 @@ func main() { flag.Parse() + service, _ := systemd.New() + conf, err := configuration.LoadConfig(*conf_file) if err != nil { fmt.Println(err) @@ -43,7 +46,9 @@ func main() { Password: *password, SSHKEY: *ssh_key, }, db) + service.Start("metadata@" + *vm_name) } else if *stop { nocloud.UnLoadNoCloudInDB(*vm_name, db) + service.Stop("metadata@" + *vm_name) } } From 44b58b36142356811cb094aa2bac44c4e8bfb71c Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Wed, 7 Jan 2026 21:06:03 +0100 Subject: [PATCH 36/84] f-11: fix: clean print not usefull #11 Signed-off-by: GnomeZworc --- cmd/meta_cli/main.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/meta_cli/main.go b/cmd/meta_cli/main.go index 9782b5f..2c7982d 100644 --- a/cmd/meta_cli/main.go +++ b/cmd/meta_cli/main.go @@ -30,7 +30,6 @@ func main() { fmt.Println(err) return } - fmt.Print(conf) db := kv.InitDB(kv.Config{ Path: conf.Database.Path, From 24e3de808891e8a65ce959d2191d220fa6fbfe16 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Wed, 7 Jan 2026 21:06:47 +0100 Subject: [PATCH 37/84] f-11: improvement: add a dryrun function Signed-off-by: GnomeZworc --- cmd/meta_cli/main.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cmd/meta_cli/main.go b/cmd/meta_cli/main.go index 2c7982d..18cf391 100644 --- a/cmd/meta_cli/main.go +++ b/cmd/meta_cli/main.go @@ -20,6 +20,7 @@ func main() { password := flag.String("pass", "", "password user") start := flag.Bool("start", false, "start metadata server") stop := flag.Bool("stop", false, "stop metadata server") + dryrun := flag.Bool("dryrun", false, "launch in dry node") flag.Parse() @@ -45,9 +46,13 @@ func main() { Password: *password, SSHKEY: *ssh_key, }, db) - service.Start("metadata@" + *vm_name) + if !*dryrun { + service.Start("metadata@" + *vm_name) + } } else if *stop { nocloud.UnLoadNoCloudInDB(*vm_name, db) - service.Stop("metadata@" + *vm_name) + if !*dryrun { + service.Stop("metadata@" + *vm_name) + } } } From 37e345609e58307487af4e616aa14ca1834680fd Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Wed, 7 Jan 2026 21:09:04 +0100 Subject: [PATCH 38/84] f-11: fix: clean error in nocloud work #11 Signed-off-by: GnomeZworc --- internal/load_db/nocloud/render.go | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/internal/load_db/nocloud/render.go b/internal/load_db/nocloud/render.go index ad7eee1..4fa9386 100644 --- a/internal/load_db/nocloud/render.go +++ b/internal/load_db/nocloud/render.go @@ -26,25 +26,21 @@ func renderConfig(path string, cfg Config) (string, error) { return buf.String(), nil } -var DB *badger.DB - func LoadNcCloudInDB(config Config, db *badger.DB) { meta_data, _ := renderConfig("templates/meta-data.tmpl", config) user_data, _ := renderConfig("templates/user-data.tmpl", config) network_config, _ := renderConfig("templates/network-config.tmpl", config) vendor_data, _ := renderConfig("templates/vendor-data.tmpl", config) - DB = db - - kv.AddInDB(DB, "metadata/"+config.Name+"/meta-data", meta_data) - kv.AddInDB(DB, "metadata/"+config.Name+"/user-data", user_data) - kv.AddInDB(DB, "metadata/"+config.Name+"/network-config", network_config) - kv.AddInDB(DB, "metadata/"+config.Name+"/vendor-data", vendor_data) - kv.AddInDB(DB, "metadata/"+config.Name+"/vpc", config.VpcName) - kv.AddInDB(DB, "metadata/"+config.Name+"/bind_ip", config.BindIP) - kv.AddInDB(DB, "metadata/"+config.Name+"/bind_port", config.BindPort) + kv.AddInDB(db, "metadata/"+config.Name+"/meta-data", meta_data) + kv.AddInDB(db, "metadata/"+config.Name+"/user-data", user_data) + kv.AddInDB(db, "metadata/"+config.Name+"/network-config", network_config) + kv.AddInDB(db, "metadata/"+config.Name+"/vendor-data", vendor_data) + kv.AddInDB(db, "metadata/"+config.Name+"/vpc", config.VpcName) + kv.AddInDB(db, "metadata/"+config.Name+"/bind_ip", config.BindIP) + kv.AddInDB(db, "metadata/"+config.Name+"/bind_port", config.BindPort) } func UnLoadNoCloudInDB(vm_name string, db *badger.DB) { - kv.DeleteInDB(DB, "metadata/"+vm_name) + kv.DeleteInDB(db, "metadata/"+vm_name) } From 9780947805438fc884327528d94f11817ca04ee3 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Wed, 7 Jan 2026 21:10:37 +0100 Subject: [PATCH 39/84] f-11: code: change struct name Signed-off-by: GnomeZworc --- cmd/metadata/main.go | 2 +- internal/metadata/server.go | 4 ++-- internal/metadata/struct.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/metadata/main.go b/cmd/metadata/main.go index 4813ed5..96e3f75 100644 --- a/cmd/metadata/main.go +++ b/cmd/metadata/main.go @@ -17,7 +17,7 @@ var ( func main() { flag.Parse() - metadata.StartServer(metadata.Config{ + metadata.StartServer(metadata.ServerConfig{ Netns: *netns_name, Iface: *iface, Port: *port, diff --git a/internal/metadata/server.go b/internal/metadata/server.go index 3a63cdb..3b4e12f 100644 --- a/internal/metadata/server.go +++ b/internal/metadata/server.go @@ -23,7 +23,7 @@ func getIP(r *http.Request) string { return ip } -func getFromDB(config Config) NoCloudData { +func getFromDB(config ServerConfig) NoCloudData { var netns_name string var port int var iface string @@ -92,7 +92,7 @@ func rootHandler(w http.ResponseWriter, r *http.Request) { } } -func StartServer(config Config) { +func StartServer(config ServerConfig) { data = getFromDB(config) if data.NetNs != "" { diff --git a/internal/metadata/struct.go b/internal/metadata/struct.go index 9e95341..f2c364e 100644 --- a/internal/metadata/struct.go +++ b/internal/metadata/struct.go @@ -10,7 +10,7 @@ type NoCloudData struct { Port int } -type Config struct { +type ServerConfig struct { Netns string File string Iface string From a8ffeebb7262f0c8804cc82eeb8e0e6703e85c03 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Wed, 7 Jan 2026 23:16:58 +0100 Subject: [PATCH 40/84] f-11: code: move start and stop metadata #11 Signed-off-by: GnomeZworc --- cmd/meta_cli/main.go | 17 +++--------- internal/load_db/nocloud/struct.go | 10 ------- internal/metadata/handle.go | 26 +++++++++++++++++++ .../{load_db/nocloud => metadata}/render.go | 14 +++++----- internal/metadata/struct.go | 9 +++++++ .../templates/meta-data.tmpl | 0 .../templates/network-config.tmpl | 0 .../templates/user-data.tmpl | 0 .../templates/vendor-data.tmpl | 0 9 files changed, 46 insertions(+), 30 deletions(-) delete mode 100644 internal/load_db/nocloud/struct.go create mode 100644 internal/metadata/handle.go rename internal/{load_db/nocloud => metadata}/render.go (70%) rename internal/{load_db/nocloud => metadata}/templates/meta-data.tmpl (100%) rename internal/{load_db/nocloud => metadata}/templates/network-config.tmpl (100%) rename internal/{load_db/nocloud => metadata}/templates/user-data.tmpl (100%) rename internal/{load_db/nocloud => metadata}/templates/vendor-data.tmpl (100%) diff --git a/cmd/meta_cli/main.go b/cmd/meta_cli/main.go index 18cf391..20706d2 100644 --- a/cmd/meta_cli/main.go +++ b/cmd/meta_cli/main.go @@ -5,9 +5,8 @@ import ( "fmt" configuration "git.g3e.fr/syonad/two/internal/config/agent" - "git.g3e.fr/syonad/two/internal/load_db/nocloud" + "git.g3e.fr/syonad/two/internal/metadata" "git.g3e.fr/syonad/two/pkg/db/kv" - "git.g3e.fr/syonad/two/pkg/systemd" ) func main() { @@ -24,8 +23,6 @@ func main() { flag.Parse() - service, _ := systemd.New() - conf, err := configuration.LoadConfig(*conf_file) if err != nil { fmt.Println(err) @@ -38,21 +35,15 @@ func main() { defer db.Close() if *start { - nocloud.LoadNcCloudInDB(nocloud.Config{ + metadata.StartMetadata(metadata.NoCloudConfig{ VpcName: *vpc, Name: *vm_name, BindIP: *bind_ip, BindPort: *bind_port, Password: *password, SSHKEY: *ssh_key, - }, db) - if !*dryrun { - service.Start("metadata@" + *vm_name) - } + }, db, *dryrun) } else if *stop { - nocloud.UnLoadNoCloudInDB(*vm_name, db) - if !*dryrun { - service.Stop("metadata@" + *vm_name) - } + metadata.StopMetadata(*vm_name, db, *dryrun) } } diff --git a/internal/load_db/nocloud/struct.go b/internal/load_db/nocloud/struct.go deleted file mode 100644 index 4134000..0000000 --- a/internal/load_db/nocloud/struct.go +++ /dev/null @@ -1,10 +0,0 @@ -package nocloud - -type Config struct { - VpcName string - BindIP string - BindPort string - Name string - Password string - SSHKEY string -} diff --git a/internal/metadata/handle.go b/internal/metadata/handle.go new file mode 100644 index 0000000..33ac088 --- /dev/null +++ b/internal/metadata/handle.go @@ -0,0 +1,26 @@ +package metadata + +import ( + "git.g3e.fr/syonad/two/pkg/systemd" + "github.com/dgraph-io/badger/v4" +) + +func StartMetadata(config NoCloudConfig, db *badger.DB, dryrun bool) { + service, _ := systemd.New() + defer service.Close() + + LoadNcCloudInDB(config, db) + if !dryrun { + service.Start("metadata@" + config.Name) + } +} + +func StopMetadata(vm_name string, db *badger.DB, dryrun bool) { + service, _ := systemd.New() + defer service.Close() + + UnLoadNoCloudInDB(vm_name, db) + if !dryrun { + service.Stop("metadata@" + vm_name) + } +} diff --git a/internal/load_db/nocloud/render.go b/internal/metadata/render.go similarity index 70% rename from internal/load_db/nocloud/render.go rename to internal/metadata/render.go index 4fa9386..f6c3cae 100644 --- a/internal/load_db/nocloud/render.go +++ b/internal/metadata/render.go @@ -1,4 +1,4 @@ -package nocloud +package metadata import ( "bytes" @@ -12,7 +12,7 @@ import ( //go:embed templates/*.tmpl var templateFS embed.FS -func renderConfig(path string, cfg Config) (string, error) { +func RenderConfig(path string, cfg NoCloudConfig) (string, error) { tpl, err := template.ParseFS(templateFS, path) if err != nil { return "", err @@ -26,11 +26,11 @@ func renderConfig(path string, cfg Config) (string, error) { return buf.String(), nil } -func LoadNcCloudInDB(config Config, db *badger.DB) { - meta_data, _ := renderConfig("templates/meta-data.tmpl", config) - user_data, _ := renderConfig("templates/user-data.tmpl", config) - network_config, _ := renderConfig("templates/network-config.tmpl", config) - vendor_data, _ := renderConfig("templates/vendor-data.tmpl", config) +func LoadNcCloudInDB(config NoCloudConfig, db *badger.DB) { + meta_data, _ := RenderConfig("templates/meta-data.tmpl", config) + user_data, _ := RenderConfig("templates/user-data.tmpl", config) + network_config, _ := RenderConfig("templates/network-config.tmpl", config) + vendor_data, _ := RenderConfig("templates/vendor-data.tmpl", config) kv.AddInDB(db, "metadata/"+config.Name+"/meta-data", meta_data) kv.AddInDB(db, "metadata/"+config.Name+"/user-data", user_data) diff --git a/internal/metadata/struct.go b/internal/metadata/struct.go index f2c364e..5810ff4 100644 --- a/internal/metadata/struct.go +++ b/internal/metadata/struct.go @@ -18,3 +18,12 @@ type ServerConfig struct { ConfFile string VmName string } + +type NoCloudConfig struct { + VpcName string + BindIP string + BindPort string + Name string + Password string + SSHKEY string +} diff --git a/internal/load_db/nocloud/templates/meta-data.tmpl b/internal/metadata/templates/meta-data.tmpl similarity index 100% rename from internal/load_db/nocloud/templates/meta-data.tmpl rename to internal/metadata/templates/meta-data.tmpl diff --git a/internal/load_db/nocloud/templates/network-config.tmpl b/internal/metadata/templates/network-config.tmpl similarity index 100% rename from internal/load_db/nocloud/templates/network-config.tmpl rename to internal/metadata/templates/network-config.tmpl diff --git a/internal/load_db/nocloud/templates/user-data.tmpl b/internal/metadata/templates/user-data.tmpl similarity index 100% rename from internal/load_db/nocloud/templates/user-data.tmpl rename to internal/metadata/templates/user-data.tmpl diff --git a/internal/load_db/nocloud/templates/vendor-data.tmpl b/internal/metadata/templates/vendor-data.tmpl similarity index 100% rename from internal/load_db/nocloud/templates/vendor-data.tmpl rename to internal/metadata/templates/vendor-data.tmpl From 07cf77ba7d04902c506e64201a2d0eb954b2c946 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Wed, 7 Jan 2026 23:24:43 +0100 Subject: [PATCH 41/84] f-11: fix: rename meta_cli to metacli Signed-off-by: GnomeZworc --- .forgejo/workflows/prerelease.yml | 2 +- cmd/{meta_cli => metacli}/main.go | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename cmd/{meta_cli => metacli}/main.go (100%) diff --git a/.forgejo/workflows/prerelease.yml b/.forgejo/workflows/prerelease.yml index c6ea322..0326071 100644 --- a/.forgejo/workflows/prerelease.yml +++ b/.forgejo/workflows/prerelease.yml @@ -32,7 +32,7 @@ jobs: matrix: goos: [linux] goarch: [amd64] - binaries: [db, metadata, meta_cli, agent] + binaries: [db, metadata, metacli, agent] uses: ./.forgejo/workflows/build.yml with: tag: ${{ needs.set-release-target.outputs.release_cible }} diff --git a/cmd/meta_cli/main.go b/cmd/metacli/main.go similarity index 100% rename from cmd/meta_cli/main.go rename to cmd/metacli/main.go From 00852ec1d3f6386f310180f3a9e256489b5525e1 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Wed, 7 Jan 2026 23:30:01 +0100 Subject: [PATCH 42/84] f-11: debug: remove debug print from db kv interface #11 Signed-off-by: GnomeZworc --- pkg/db/kv/deleteInDB.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/db/kv/deleteInDB.go b/pkg/db/kv/deleteInDB.go index 36d8669..1943c81 100644 --- a/pkg/db/kv/deleteInDB.go +++ b/pkg/db/kv/deleteInDB.go @@ -1,7 +1,6 @@ package kv import ( - "fmt" "log" "github.com/dgraph-io/badger/v4" @@ -29,7 +28,6 @@ func DeleteInDB(db *badger.DB, key string) error { key := item.Key() k := append([]byte{}, key...) - fmt.Println(string(k)) if err := deleteKey(db, string(k)); err != nil { return err } From 454005d6ac65ea798d7ad72e62ce27dfff485698 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Thu, 8 Jan 2026 23:37:35 +0100 Subject: [PATCH 43/84] f-10: code: add create and delete netns #10 Signed-off-by: GnomeZworc --- internal/netns/create.go | 5 ++++ internal/netns/create_linux.go | 55 ++++++++++++++++++++++++++++++++++ internal/netns/create_other.go | 5 ++++ internal/netns/delete.go | 5 ++++ internal/netns/delete_linux.go | 18 +++++++++++ internal/netns/delete_other.go | 5 ++++ internal/netns/exist.go | 14 +++++++++ 7 files changed, 107 insertions(+) create mode 100644 internal/netns/create.go create mode 100644 internal/netns/create_linux.go create mode 100644 internal/netns/create_other.go create mode 100644 internal/netns/delete.go create mode 100644 internal/netns/delete_linux.go create mode 100644 internal/netns/delete_other.go create mode 100644 internal/netns/exist.go diff --git a/internal/netns/create.go b/internal/netns/create.go new file mode 100644 index 0000000..d6ca305 --- /dev/null +++ b/internal/netns/create.go @@ -0,0 +1,5 @@ +package netns + +func Create(name string) error { + return create(name) +} diff --git a/internal/netns/create_linux.go b/internal/netns/create_linux.go new file mode 100644 index 0000000..8ee0afa --- /dev/null +++ b/internal/netns/create_linux.go @@ -0,0 +1,55 @@ +//go:build linux + +package netns + +import ( + "os" + + "golang.org/x/sys/unix" +) + +func create(name string) error { + base := "/var/run/netns" + path := base + "/" + name + + if err := os.MkdirAll(base, 0755); err != nil { + return err + } + + // fichier cible + f, err := os.Create(path) + if err != nil { + return err + } + f.Close() + + // sauvegarde du netns courant + orig, err := os.Open("/proc/self/ns/net") + if err != nil { + return err + } + defer orig.Close() + + // nouveau netns + if err := unix.Unshare(unix.CLONE_NEWNET); err != nil { + return err + } + + // bind mount du netns courant vers /var/run/netns/ + if err := unix.Mount( + "/proc/self/ns/net", + path, + "", + unix.MS_BIND, + "", + ); err != nil { + return err + } + + // revenir au netns original + if err := unix.Setns(int(orig.Fd()), unix.CLONE_NEWNET); err != nil { + return err + } + + return nil +} diff --git a/internal/netns/create_other.go b/internal/netns/create_other.go new file mode 100644 index 0000000..3c0fd66 --- /dev/null +++ b/internal/netns/create_other.go @@ -0,0 +1,5 @@ +//go:build !linux + +package netns + +func create(string) error { return nil } diff --git a/internal/netns/delete.go b/internal/netns/delete.go new file mode 100644 index 0000000..af0a585 --- /dev/null +++ b/internal/netns/delete.go @@ -0,0 +1,5 @@ +package netns + +func Delete(name string) error { + return delete(name) +} diff --git a/internal/netns/delete_linux.go b/internal/netns/delete_linux.go new file mode 100644 index 0000000..ac0af1f --- /dev/null +++ b/internal/netns/delete_linux.go @@ -0,0 +1,18 @@ +//go:build linux + +package netns + +import ( + "os" + + "golang.org/x/sys/unix" +) + +func delete(name string) error { + path := "/var/run/netns/" + name + + if err := unix.Unmount(path, unix.MNT_DETACH); err != nil { + return err + } + return os.Remove(path) +} diff --git a/internal/netns/delete_other.go b/internal/netns/delete_other.go new file mode 100644 index 0000000..186d355 --- /dev/null +++ b/internal/netns/delete_other.go @@ -0,0 +1,5 @@ +//go:build !linux + +package netns + +func delete(string) error { return nil } diff --git a/internal/netns/exist.go b/internal/netns/exist.go new file mode 100644 index 0000000..7783dd9 --- /dev/null +++ b/internal/netns/exist.go @@ -0,0 +1,14 @@ +package netns + +import ( + "os" +) + +func exist(name string) bool { + _, err := os.Stat("/var/run/netns/" + name) + return err == nil +} + +func Exist(name string) bool { + return exist(name) +} From a650a34fc3a424075c48ec9d88b4d77bf1735fe4 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Thu, 8 Jan 2026 23:47:52 +0100 Subject: [PATCH 44/84] f-10: code: add function to call a function in a netns #10 Signed-off-by: GnomeZworc --- internal/netns/call.go | 5 ++++ internal/netns/call_linux.go | 44 ++++++++++++++++++++++++++++++++++++ internal/netns/call_other.go | 7 ++++++ 3 files changed, 56 insertions(+) create mode 100644 internal/netns/call.go create mode 100644 internal/netns/call_linux.go create mode 100644 internal/netns/call_other.go diff --git a/internal/netns/call.go b/internal/netns/call.go new file mode 100644 index 0000000..da326fd --- /dev/null +++ b/internal/netns/call.go @@ -0,0 +1,5 @@ +package netns + +func Call(name string, fn func() error) error { + return call(name, fn) +} diff --git a/internal/netns/call_linux.go b/internal/netns/call_linux.go new file mode 100644 index 0000000..b23ac19 --- /dev/null +++ b/internal/netns/call_linux.go @@ -0,0 +1,44 @@ +//go:build linux + +package netns + +import ( + "fmt" + "os" + "runtime" + + "golang.org/x/sys/unix" +) + +func call(name string, fn func() error) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + // sauvegarde du netns courant + orig, err := os.Open("/proc/self/ns/net") + if err != nil { + return err + } + defer orig.Close() + + // entrer dans le netns cible + f, err := os.Open(fmt.Sprintf("/var/run/netns/%s", name)) + if err != nil { + return err + } + defer f.Close() + + if err := unix.Setns(int(f.Fd()), unix.CLONE_NEWNET); err != nil { + return err + } + + // exécuter la fonction dans le netns + err = fn() + + // toujours revenir au netns d'origine + if restoreErr := unix.Setns(int(orig.Fd()), unix.CLONE_NEWNET); restoreErr != nil { + return restoreErr + } + + return err +} diff --git a/internal/netns/call_other.go b/internal/netns/call_other.go new file mode 100644 index 0000000..14a8924 --- /dev/null +++ b/internal/netns/call_other.go @@ -0,0 +1,7 @@ +//go:build !linux + +package netns + +func call(name string, fn func() error) error { + return fn() +} From 0d3d59a019bcc81aa5532f076339214c40cf01c9 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Thu, 8 Jan 2026 23:57:35 +0100 Subject: [PATCH 45/84] f-10: bin: add a new binarie #10 Signed-off-by: GnomeZworc --- .forgejo/workflows/prerelease.yml | 2 +- cmd/netns/main.go | 40 +++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 cmd/netns/main.go diff --git a/.forgejo/workflows/prerelease.yml b/.forgejo/workflows/prerelease.yml index 0326071..0248db4 100644 --- a/.forgejo/workflows/prerelease.yml +++ b/.forgejo/workflows/prerelease.yml @@ -32,7 +32,7 @@ jobs: matrix: goos: [linux] goarch: [amd64] - binaries: [db, metadata, metacli, agent] + binaries: [db, metadata, metacli, agent, netns] uses: ./.forgejo/workflows/build.yml with: tag: ${{ needs.set-release-target.outputs.release_cible }} diff --git a/cmd/netns/main.go b/cmd/netns/main.go new file mode 100644 index 0000000..e1729f7 --- /dev/null +++ b/cmd/netns/main.go @@ -0,0 +1,40 @@ +package main + +import ( + "flag" + "fmt" + "os" + + "git.g3e.fr/syonad/two/internal/netns" +) + +var ( + netns_name = flag.String("netns", "", "Network namespace à faire") + action = flag.String("action", "", "Action a faire") +) + +func main() { + flag.Parse() + + switch *action { + case "create": + err := netns.Create(*netns_name) + if err != nil { + fmt.Println(err) + } + case "delete": + err := netns.Delete(*netns_name) + if err != nil { + fmt.Println(err) + } + case "check": + if netns.Exist(*netns_name) { + fmt.Printf("netns %s exist\n", *netns_name) + } else { + fmt.Printf("netns %s do not exist\n", *netns_name) + } + default: + fmt.Printf("Available commande:\n - create\n - delete\n - check\n") + os.Exit(1) + } +} From d1559b2ba070400ce6fadd0efe80d36b1ba7623e Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Thu, 8 Jan 2026 23:58:31 +0100 Subject: [PATCH 46/84] f-10: ci: change ci format #10 Signed-off-by: GnomeZworc --- .forgejo/workflows/prerelease.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.forgejo/workflows/prerelease.yml b/.forgejo/workflows/prerelease.yml index 0248db4..42d3a75 100644 --- a/.forgejo/workflows/prerelease.yml +++ b/.forgejo/workflows/prerelease.yml @@ -32,7 +32,12 @@ jobs: matrix: goos: [linux] goarch: [amd64] - binaries: [db, metadata, metacli, agent, netns] + binaries: + - db + - metadata + - metacli + - agent + - netns uses: ./.forgejo/workflows/build.yml with: tag: ${{ needs.set-release-target.outputs.release_cible }} From fc62d5a524e50de92270c92ad657900c43366cf1 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Sun, 11 Jan 2026 18:37:31 +0100 Subject: [PATCH 47/84] f-14: package: add a new package Signed-off-by: GnomeZworc --- go.mod | 2 ++ go.sum | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/go.mod b/go.mod index 26bd137..7430e72 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,8 @@ require ( github.com/spf13/pflag v1.0.10 // indirect github.com/spf13/viper v1.21.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect + github.com/vishvananda/netlink v1.3.1 // indirect + github.com/vishvananda/netns v0.0.5 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/otel v1.37.0 // indirect go.opentelemetry.io/otel/metric v1.37.0 // indirect diff --git a/go.sum b/go.sum index 0d19489..73d16f3 100644 --- a/go.sum +++ b/go.sum @@ -39,6 +39,10 @@ github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0= +github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4= +github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= +github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= @@ -51,6 +55,8 @@ go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= From 15a913643edab26206c4a444691150edb79022ea Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Sun, 11 Jan 2026 18:38:02 +0100 Subject: [PATCH 48/84] f-14: code: add a new package internal #14 Signed-off-by: GnomeZworc --- internal/netif/veth.go | 48 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 internal/netif/veth.go diff --git a/internal/netif/veth.go b/internal/netif/veth.go new file mode 100644 index 0000000..0056c60 --- /dev/null +++ b/internal/netif/veth.go @@ -0,0 +1,48 @@ +package netif + +import ( + "fmt" + "runtime" + + "github.com/vishvananda/netlink" + "github.com/vishvananda/netns" +) + +func CreateVethToNetns(rootIf, nsIf, netnsPath string, mtu int) error { + // Obligatoire : netns lié au thread + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + // Ouvrir le netns cible + ns, err := netns.GetFromPath(netnsPath) + if err != nil { + return fmt.Errorf("open netns: %w, %s", err, netnsPath) + } + defer ns.Close() + + // Créer le veth dans le netns courant + veth := &netlink.Veth{ + LinkAttrs: netlink.LinkAttrs{ + Name: rootIf, + MTU: mtu, + }, + PeerName: nsIf, + } + + if err := netlink.LinkAdd(veth); err != nil { + return fmt.Errorf("link add: %w", err) + } + + // Récupérer l'interface peer + peer, err := netlink.LinkByName(nsIf) + if err != nil { + return fmt.Errorf("peer not found: %w", err) + } + + // Déplacer le peer dans le netns cible + if err := netlink.LinkSetNsFd(peer, int(ns)); err != nil { + return fmt.Errorf("set ns: %w", err) + } + + return nil +} From 5a38e09b85dbca1b228e94de0f13671eae26d4ec Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Sun, 11 Jan 2026 18:38:59 +0100 Subject: [PATCH 49/84] f-14: bin: add a new binarie for test #14 Signed-off-by: GnomeZworc --- cmd/vpc/main.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 cmd/vpc/main.go diff --git a/cmd/vpc/main.go b/cmd/vpc/main.go new file mode 100644 index 0000000..d18dabd --- /dev/null +++ b/cmd/vpc/main.go @@ -0,0 +1,30 @@ +package main + +import ( + "flag" + "fmt" + "os" + + "git.g3e.fr/syonad/two/internal/netif" +) + +var ( + netns = flag.String("netns", "", "Network namespace à faire") + name = flag.String("name", "", "interface name") + action = flag.String("action", "", "Action a faire") +) + +func main() { + flag.Parse() + + switch *action { + case "create": + err := netif.CreateVethToNetns("veth"+*name+"ext", "veth"+*name+"int", "/var/run/netns/"+*netns, 9000) + if err != nil { + fmt.Println(err) + } + default: + fmt.Printf("Available commande:\n - create\n - delete\n - check\n") + os.Exit(1) + } +} From 9420a9f7b4e881fbeeda3769592391f4ab49dcfc Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Tue, 13 Jan 2026 10:53:43 +0100 Subject: [PATCH 50/84] f-14: code: add create bridge code #14 Signed-off-by: GnomeZworc --- internal/netif/bridge.go | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 internal/netif/bridge.go diff --git a/internal/netif/bridge.go b/internal/netif/bridge.go new file mode 100644 index 0000000..d6dea70 --- /dev/null +++ b/internal/netif/bridge.go @@ -0,0 +1,38 @@ +package netif + +import ( + "fmt" + "os" + + "github.com/vishvananda/netlink" +) + +func setBridgeSTP(bridge string, enable bool) error { + path := fmt.Sprintf("/sys/class/net/%s/bridge/stp_state", bridge) + + val := "0" + if enable { + val = "1" + } + + return os.WriteFile(path, []byte(val), 0644) +} + +func CreateBridge(name string, mtu int, stp bool) error { + br := &netlink.Bridge{ + LinkAttrs: netlink.LinkAttrs{ + Name: name, + MTU: mtu, + }, + } + + if err := netlink.LinkAdd(br); err != nil { + return err + } + + if err := netlink.LinkSetUp(br); err != nil { + return err + } + + return setBridgeSTP(name, stp) +} From fd4c7e9a3a6133c0f8f547d65a0b5239a3a63ca3 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Thu, 15 Jan 2026 22:54:22 +0100 Subject: [PATCH 51/84] f-14: code: add an up and down ip #14 Signed-off-by: GnomeZworc --- internal/netif/upDown.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 internal/netif/upDown.go diff --git a/internal/netif/upDown.go b/internal/netif/upDown.go new file mode 100644 index 0000000..6d27cc2 --- /dev/null +++ b/internal/netif/upDown.go @@ -0,0 +1,21 @@ +package netif + +import ( + "github.com/vishvananda/netlink" +) + +func LinkSetUp(name string) error { + link, err := netlink.LinkByName(name) + if err != nil { + return err + } + return netlink.LinkSetUp(link) +} + +func LinkSetDown(name string) error { + link, err := netlink.LinkByName(name) + if err != nil { + return err + } + return netlink.LinkSetDown(link) +} From db222b383cd9b4b17e5676e3dd507ccb27a1fe70 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Fri, 16 Jan 2026 22:15:23 +0100 Subject: [PATCH 52/84] f-14: code: add delete interface function #14 Signed-off-by: GnomeZworc --- internal/netif/delete.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 internal/netif/delete.go diff --git a/internal/netif/delete.go b/internal/netif/delete.go new file mode 100644 index 0000000..9b06c55 --- /dev/null +++ b/internal/netif/delete.go @@ -0,0 +1,13 @@ +package netif + +import ( + "github.com/vishvananda/netlink" +) + +func DeleteLink(name string) error { + link, err := netlink.LinkByName(name) + if err != nil { + return err + } + return netlink.LinkDel(link) +} From d3b471b24aefe303c98dae259c5e44719e65cf56 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Sat, 17 Jan 2026 20:59:05 +0100 Subject: [PATCH 53/84] f-14: code: add set master bridge #14 Signed-off-by: GnomeZworc --- internal/netif/bridge.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/internal/netif/bridge.go b/internal/netif/bridge.go index d6dea70..daf21ec 100644 --- a/internal/netif/bridge.go +++ b/internal/netif/bridge.go @@ -36,3 +36,17 @@ func CreateBridge(name string, mtu int, stp bool) error { return setBridgeSTP(name, stp) } + +func BridgeSetMaster(iface, bridge string) error { + link, err := netlink.LinkByName(iface) + if err != nil { + return err + } + + br, err := netlink.LinkByName(bridge) + if err != nil { + return err + } + + return netlink.LinkSetMaster(link, br) +} From 9fb4d10d270c1ea50a9dc11dabd67d20c90dc374 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Sun, 18 Jan 2026 22:28:38 +0100 Subject: [PATCH 54/84] f-14: code: add create and delete vpc #14 Signed-off-by: GnomeZworc --- internal/vpc/create.go | 60 ++++++++++++++++++++++++++++++++++++++++++ internal/vpc/delete.go | 18 +++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 internal/vpc/create.go create mode 100644 internal/vpc/delete.go diff --git a/internal/vpc/create.go b/internal/vpc/create.go new file mode 100644 index 0000000..55896ba --- /dev/null +++ b/internal/vpc/create.go @@ -0,0 +1,60 @@ +package vpc + +import ( + "git.g3e.fr/syonad/two/internal/netif" + "git.g3e.fr/syonad/two/internal/netns" +) + +func CreateVPC(name string) error { + // missing + // search data in db + // change state in db + + // create netns + if err := netns.Create(name); err != nil { + return err + } + + // create veth public for this netns + if err := netif.CreateVethToNetns(name+"-ext", "veth-"+name+"-int", "/var/run/netns/"+name, 9000); err != nil { + return err + } + + // create public bridge in netns + if err := netns.Call(name, func() error { + return netif.CreateBridge("br-public", 1500, false) + }); err != nil { + return err + } + + // set veth to ext public bridge + if err := netif.BridgeSetMaster(name+"-ext", "br-public"); err != nil { + return err + } + + // set veth to int public bridge + if err := netns.Call(name, func() error { + return netif.BridgeSetMaster("veth-"+name+"-int", "br-public") + }); err != nil { + return err + } + + // set set ext veth up + if err := netif.LinkSetUp(name + "-ext"); err != nil { + return nil + } + // set set int veth up + if err := netns.Call(name, func() error { + return netif.LinkSetUp("veth-" + name + "-int") + }); err != nil { + return err + } + // set set int bridge up + if err := netns.Call(name, func() error { + return netif.LinkSetUp("br-public") + }); err != nil { + return err + } + + return nil +} diff --git a/internal/vpc/delete.go b/internal/vpc/delete.go new file mode 100644 index 0000000..3daea61 --- /dev/null +++ b/internal/vpc/delete.go @@ -0,0 +1,18 @@ +package vpc + +import ( + "git.g3e.fr/syonad/two/internal/netif" + "git.g3e.fr/syonad/two/internal/netns" +) + +func DeleteVPC(name string) error { + if err := netif.DeleteLink(name + "-ext"); err != nil { + return err + } + + if err := netns.Delete(name); err != nil { + return err + } + + return nil +} From d050568638e6bd5642a2d3dcb494a9f8984f7a01 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Sun, 18 Jan 2026 22:30:38 +0100 Subject: [PATCH 55/84] f-14: bin: update bin #14 Signed-off-by: GnomeZworc --- cmd/vpc/main.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/cmd/vpc/main.go b/cmd/vpc/main.go index d18dabd..6d70f79 100644 --- a/cmd/vpc/main.go +++ b/cmd/vpc/main.go @@ -5,7 +5,7 @@ import ( "fmt" "os" - "git.g3e.fr/syonad/two/internal/netif" + "git.g3e.fr/syonad/two/internal/vpc" ) var ( @@ -19,8 +19,11 @@ func main() { switch *action { case "create": - err := netif.CreateVethToNetns("veth"+*name+"ext", "veth"+*name+"int", "/var/run/netns/"+*netns, 9000) - if err != nil { + if err := vpc.CreateVPC(*name); err != nil { + fmt.Println(err) + } + case "delete": + if err := vpc.DeleteVPC(*name); err != nil { fmt.Println(err) } default: From 0b797d1c0c3c2389aa581122a3bed9e1bebd6e6a Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Sun, 18 Jan 2026 22:32:21 +0100 Subject: [PATCH 56/84] f-14: bin: remove netns bin #14 Signed-off-by: GnomeZworc --- .forgejo/workflows/prerelease.yml | 1 - cmd/netns/main.go | 40 ------------------------------- 2 files changed, 41 deletions(-) delete mode 100644 cmd/netns/main.go diff --git a/.forgejo/workflows/prerelease.yml b/.forgejo/workflows/prerelease.yml index 42d3a75..ee046e2 100644 --- a/.forgejo/workflows/prerelease.yml +++ b/.forgejo/workflows/prerelease.yml @@ -37,7 +37,6 @@ jobs: - metadata - metacli - agent - - netns uses: ./.forgejo/workflows/build.yml with: tag: ${{ needs.set-release-target.outputs.release_cible }} diff --git a/cmd/netns/main.go b/cmd/netns/main.go deleted file mode 100644 index e1729f7..0000000 --- a/cmd/netns/main.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "os" - - "git.g3e.fr/syonad/two/internal/netns" -) - -var ( - netns_name = flag.String("netns", "", "Network namespace à faire") - action = flag.String("action", "", "Action a faire") -) - -func main() { - flag.Parse() - - switch *action { - case "create": - err := netns.Create(*netns_name) - if err != nil { - fmt.Println(err) - } - case "delete": - err := netns.Delete(*netns_name) - if err != nil { - fmt.Println(err) - } - case "check": - if netns.Exist(*netns_name) { - fmt.Printf("netns %s exist\n", *netns_name) - } else { - fmt.Printf("netns %s do not exist\n", *netns_name) - } - default: - fmt.Printf("Available commande:\n - create\n - delete\n - check\n") - os.Exit(1) - } -} From 6f40b3e9219bdc6f1d34a466fae5dd062d35b038 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Sun, 18 Jan 2026 22:32:49 +0100 Subject: [PATCH 57/84] f-14: code: add vpc in prerelease bin #14 Signed-off-by: GnomeZworc --- .forgejo/workflows/prerelease.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.forgejo/workflows/prerelease.yml b/.forgejo/workflows/prerelease.yml index ee046e2..f20ca66 100644 --- a/.forgejo/workflows/prerelease.yml +++ b/.forgejo/workflows/prerelease.yml @@ -37,6 +37,7 @@ jobs: - metadata - metacli - agent + - vpc uses: ./.forgejo/workflows/build.yml with: tag: ${{ needs.set-release-target.outputs.release_cible }} From 2ea3c6dd14ba65e99e902747356d608b422ef4e5 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Mon, 19 Jan 2026 22:41:39 +0100 Subject: [PATCH 58/84] f-14: fix: rename interface name Signed-off-by: GnomeZworc --- internal/vpc/create.go | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/internal/vpc/create.go b/internal/vpc/create.go index 55896ba..5ff5fa5 100644 --- a/internal/vpc/create.go +++ b/internal/vpc/create.go @@ -16,42 +16,36 @@ func CreateVPC(name string) error { } // create veth public for this netns - if err := netif.CreateVethToNetns(name+"-ext", "veth-"+name+"-int", "/var/run/netns/"+name, 9000); err != nil { + if err := netif.CreateVethToNetns("veth"+name+"ext", "vethpublicint", "/var/run/netns/"+name, 9000); err != nil { return err } // create public bridge in netns if err := netns.Call(name, func() error { - return netif.CreateBridge("br-public", 1500, false) + return netif.CreateBridge("br-public", 1500) }); err != nil { return err } // set veth to ext public bridge - if err := netif.BridgeSetMaster(name+"-ext", "br-public"); err != nil { + if err := netif.BridgeSetMaster("veth"+name+"ext", "br-public"); err != nil { return err } // set veth to int public bridge if err := netns.Call(name, func() error { - return netif.BridgeSetMaster("veth-"+name+"-int", "br-public") + return netif.BridgeSetMaster("vethpublicint", "br-public") }); err != nil { return err } // set set ext veth up - if err := netif.LinkSetUp(name + "-ext"); err != nil { + if err := netif.LinkSetUp("veth" + name + "ext"); err != nil { return nil } // set set int veth up if err := netns.Call(name, func() error { - return netif.LinkSetUp("veth-" + name + "-int") - }); err != nil { - return err - } - // set set int bridge up - if err := netns.Call(name, func() error { - return netif.LinkSetUp("br-public") + return netif.LinkSetUp("vethpublicint") }); err != nil { return err } From 44f01c23735da8c6e97e2f30072bd8ad6fbbd11e Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Mon, 19 Jan 2026 22:42:13 +0100 Subject: [PATCH 59/84] f-14: clean: remove not used setBridgeSTP Signed-off-by: GnomeZworc --- internal/netif/bridge.go | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/internal/netif/bridge.go b/internal/netif/bridge.go index daf21ec..a4754a3 100644 --- a/internal/netif/bridge.go +++ b/internal/netif/bridge.go @@ -1,24 +1,10 @@ package netif import ( - "fmt" - "os" - "github.com/vishvananda/netlink" ) -func setBridgeSTP(bridge string, enable bool) error { - path := fmt.Sprintf("/sys/class/net/%s/bridge/stp_state", bridge) - - val := "0" - if enable { - val = "1" - } - - return os.WriteFile(path, []byte(val), 0644) -} - -func CreateBridge(name string, mtu int, stp bool) error { +func CreateBridge(name string, mtu int) error { br := &netlink.Bridge{ LinkAttrs: netlink.LinkAttrs{ Name: name, @@ -34,7 +20,7 @@ func CreateBridge(name string, mtu int, stp bool) error { return err } - return setBridgeSTP(name, stp) + return nil } func BridgeSetMaster(iface, bridge string) error { From f9c1cd7d328f13df2c0574691b0eaae46fba24c4 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Mon, 26 Jan 2026 20:10:56 +0100 Subject: [PATCH 60/84] f-14: code: add db usage in vpc creation #14 Signed-off-by: GnomeZworc --- cmd/vpc/main.go | 26 ++++++++++++--- internal/vpc/create.go | 73 +++++++++++++++++++++++------------------- 2 files changed, 62 insertions(+), 37 deletions(-) diff --git a/cmd/vpc/main.go b/cmd/vpc/main.go index 6d70f79..8f98a46 100644 --- a/cmd/vpc/main.go +++ b/cmd/vpc/main.go @@ -5,21 +5,39 @@ import ( "fmt" "os" + configuration "git.g3e.fr/syonad/two/internal/config/agent" "git.g3e.fr/syonad/two/internal/vpc" + "git.g3e.fr/syonad/two/pkg/db/kv" + "github.com/dgraph-io/badger/v4" ) var ( - netns = flag.String("netns", "", "Network namespace à faire") - name = flag.String("name", "", "interface name") - action = flag.String("action", "", "Action a faire") + netns = flag.String("netns", "", "Network namespace à faire") + name = flag.String("name", "", "interface name") + action = flag.String("action", "", "Action a faire") + conf_file = flag.String("conf", "/etc/two/agent.yml", "configuration file") ) +var DB *badger.DB + func main() { flag.Parse() + conf, err := configuration.LoadConfig(*conf_file) + if err != nil { + fmt.Println(err) + return + } + + DB = kv.InitDB(kv.Config{ + Path: conf.Database.Path, + }, false) + defer DB.Close() + switch *action { case "create": - if err := vpc.CreateVPC(*name); err != nil { + kv.AddInDB(DB, "vpc/"+*name+"/state", "creating") + if err := vpc.CreateVPC(DB, *name); err != nil { fmt.Println(err) } case "delete": diff --git a/internal/vpc/create.go b/internal/vpc/create.go index 5ff5fa5..3c3ad87 100644 --- a/internal/vpc/create.go +++ b/internal/vpc/create.go @@ -3,52 +3,59 @@ package vpc import ( "git.g3e.fr/syonad/two/internal/netif" "git.g3e.fr/syonad/two/internal/netns" + "git.g3e.fr/syonad/two/pkg/db/kv" + + "github.com/dgraph-io/badger/v4" ) -func CreateVPC(name string) error { +func CreateVPC(db *badger.DB, name string) error { // missing // search data in db // change state in db // create netns - if err := netns.Create(name); err != nil { + if state, err := kv.GetFromDB(db, "vpc/"+name+"/state"); err != nil { return err - } + } else if state == "creating" { + if err := netns.Create(name); err != nil { + return err + } - // create veth public for this netns - if err := netif.CreateVethToNetns("veth"+name+"ext", "vethpublicint", "/var/run/netns/"+name, 9000); err != nil { - return err - } + // create veth public for this netns + if err := netif.CreateVethToNetns("veth"+name+"ext", "vethpublicint", "/var/run/netns/"+name, 9000); err != nil { + return err + } - // create public bridge in netns - if err := netns.Call(name, func() error { - return netif.CreateBridge("br-public", 1500) - }); err != nil { - return err - } + // create public bridge in netns + if err := netns.Call(name, func() error { + return netif.CreateBridge("br-public", 1500) + }); err != nil { + return err + } - // set veth to ext public bridge - if err := netif.BridgeSetMaster("veth"+name+"ext", "br-public"); err != nil { - return err - } + // set veth to ext public bridge + if err := netif.BridgeSetMaster("veth"+name+"ext", "br-public"); err != nil { + return err + } - // set veth to int public bridge - if err := netns.Call(name, func() error { - return netif.BridgeSetMaster("vethpublicint", "br-public") - }); err != nil { - return err - } + // set veth to int public bridge + if err := netns.Call(name, func() error { + return netif.BridgeSetMaster("vethpublicint", "br-public") + }); err != nil { + return err + } - // set set ext veth up - if err := netif.LinkSetUp("veth" + name + "ext"); err != nil { - return nil + // set set ext veth up + if err := netif.LinkSetUp("veth" + name + "ext"); err != nil { + return nil + } + // set set int veth up + if err := netns.Call(name, func() error { + return netif.LinkSetUp("vethpublicint") + }); err != nil { + return err + } + kv.AddInDB(db, "vpc/"+name+"/state", "created") } - // set set int veth up - if err := netns.Call(name, func() error { - return netif.LinkSetUp("vethpublicint") - }); err != nil { - return err - } - return nil } From 0b9714ce10d5a445b653f39fa2cdd6384ced86c3 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Tue, 27 Jan 2026 22:24:47 +0100 Subject: [PATCH 61/84] f-14: code: add delete operation #14 Signed-off-by: GnomeZworc --- cmd/vpc/main.go | 9 ++++++++- internal/vpc/delete.go | 18 +++++++++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/cmd/vpc/main.go b/cmd/vpc/main.go index 8f98a46..52d8133 100644 --- a/cmd/vpc/main.go +++ b/cmd/vpc/main.go @@ -41,9 +41,16 @@ func main() { fmt.Println(err) } case "delete": - if err := vpc.DeleteVPC(*name); err != nil { + kv.AddInDB(DB, "vpc/"+*name+"/state", "deleting") + if err := vpc.DeleteVPC(DB, *name); err != nil { fmt.Println(err) } + if state, err := kv.GetFromDB(DB, "vpc/"+*name+"/state"); err != nil { + fmt.Println(err) + os.Exit(1) + } else if state == "deleted" { + kv.DeleteInDB(DB, "vpc/"+*name) + } default: fmt.Printf("Available commande:\n - create\n - delete\n - check\n") os.Exit(1) diff --git a/internal/vpc/delete.go b/internal/vpc/delete.go index 3daea61..a05e807 100644 --- a/internal/vpc/delete.go +++ b/internal/vpc/delete.go @@ -3,15 +3,23 @@ package vpc import ( "git.g3e.fr/syonad/two/internal/netif" "git.g3e.fr/syonad/two/internal/netns" + "git.g3e.fr/syonad/two/pkg/db/kv" + + "github.com/dgraph-io/badger/v4" ) -func DeleteVPC(name string) error { - if err := netif.DeleteLink(name + "-ext"); err != nil { +func DeleteVPC(db *badger.DB, name string) error { + if state, err := kv.GetFromDB(db, "vpc/"+name+"/state"); err != nil { return err - } + } else if state == "deleting" { + if err := netif.DeleteLink(name + "-ext"); err != nil { + return err + } - if err := netns.Delete(name); err != nil { - return err + if err := netns.Delete(name); err != nil { + return err + } + kv.AddInDB(db, "vpc/"+name+"/state", "deleted") } return nil From 88787fd8f7ad3f86dafe118eaa65461bf6dc6bfc Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Mon, 23 Mar 2026 18:24:06 +0100 Subject: [PATCH 62/84] f-14: code: add check feature Signed-off-by: GnomeZworc --- cmd/vpc/main.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmd/vpc/main.go b/cmd/vpc/main.go index 52d8133..58e4242 100644 --- a/cmd/vpc/main.go +++ b/cmd/vpc/main.go @@ -51,6 +51,12 @@ func main() { } else if state == "deleted" { kv.DeleteInDB(DB, "vpc/"+*name) } + case "check": + if state, err := kv.GetFromDB(DB, "vpc/"+*name+"/state"); err != nil { + os.Exit(1) + } else if state == "created" { + os.Exit(1) + } default: fmt.Printf("Available commande:\n - create\n - delete\n - check\n") os.Exit(1) From c1b9b3faa4131b27c094ca03079962f3643fb258 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Tue, 24 Mar 2026 20:24:37 +0100 Subject: [PATCH 63/84] f-14: fix: add return code Signed-off-by: GnomeZworc --- cmd/vpc/main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/vpc/main.go b/cmd/vpc/main.go index 58e4242..708054c 100644 --- a/cmd/vpc/main.go +++ b/cmd/vpc/main.go @@ -61,4 +61,5 @@ func main() { fmt.Printf("Available commande:\n - create\n - delete\n - check\n") os.Exit(1) } + os.Exit(0) } From d3402dd163c7c277617b566b2aba68604ac96a0e Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Tue, 24 Mar 2026 20:28:49 +0100 Subject: [PATCH 64/84] f-14: fix: change return code Signed-off-by: GnomeZworc --- cmd/vpc/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/vpc/main.go b/cmd/vpc/main.go index 708054c..e73f7e9 100644 --- a/cmd/vpc/main.go +++ b/cmd/vpc/main.go @@ -54,7 +54,7 @@ func main() { case "check": if state, err := kv.GetFromDB(DB, "vpc/"+*name+"/state"); err != nil { os.Exit(1) - } else if state == "created" { + } else if state != "created" { os.Exit(1) } default: From 5d980514b8ea66d06d2052aa0837be6ddc4850f7 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Mon, 30 Mar 2026 23:16:52 +0200 Subject: [PATCH 65/84] f-15: code: add generate dhcp file Signed-off-by: GnomeZworc --- internal/dhcp/generate.go | 47 +++++++++++++++++++++++++++++++++++++++ internal/dhcp/struct.go | 12 ++++++++++ 2 files changed, 59 insertions(+) create mode 100644 internal/dhcp/generate.go create mode 100644 internal/dhcp/struct.go diff --git a/internal/dhcp/generate.go b/internal/dhcp/generate.go new file mode 100644 index 0000000..01bfe20 --- /dev/null +++ b/internal/dhcp/generate.go @@ -0,0 +1,47 @@ +package dhcp + +import ( + "fmt" + "net" + "os" + "path/filepath" + "strings" +) + +func GenerateConfig(c Config) (string, error) { + mask := fmt.Sprintf("%d.%d.%d.%d", c.Network.Mask[0], c.Network.Mask[1], c.Network.Mask[2], c.Network.Mask[3]) + + var sb strings.Builder + fmt.Fprintf(&sb, "no-resolv\n") + fmt.Fprintf(&sb, "dhcp-range=%s,static,%s,12h\n", c.Network.IP.String(), mask) + fmt.Fprintf(&sb, "dhcp-option=3,%s\n", c.Gateway.String()) + fmt.Fprintf(&sb, "dhcp-option=6,1.1.1.1,8.8.8.8\n\n") + + i := 0 + for ip := cloneIP(c.Network.IP); c.Network.Contains(ip); incrementIP(ip) { + fmt.Fprintf(&sb, "dhcp-host=00:22:33:%02X:%02X:%02X,%s\n", + (i>>16)&0xFF, (i>>8)&0xFF, i&0xFF, ip) + i++ + } + + outPath := filepath.Join(c.ConfDir, c.Name+".conf") + if err := os.MkdirAll(c.ConfDir, 0755); err != nil { + return "", err + } + return outPath, os.WriteFile(outPath, []byte(sb.String()), 0644) +} + +func incrementIP(ip net.IP) { + for j := len(ip) - 1; j >= 0; j-- { + ip[j]++ + if ip[j] != 0 { + break + } + } +} + +func cloneIP(ip net.IP) net.IP { + clone := make(net.IP, len(ip)) + copy(clone, ip) + return clone +} diff --git a/internal/dhcp/struct.go b/internal/dhcp/struct.go new file mode 100644 index 0000000..4c69b9c --- /dev/null +++ b/internal/dhcp/struct.go @@ -0,0 +1,12 @@ +package dhcp + +import ( + "net" +) + +type Config struct { + Network *net.IPNet + Gateway net.IP + Name string + ConfDir string +} From a346876cfb8e308d2e3aed526b01f0bb173ea67e Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Mon, 30 Mar 2026 23:19:34 +0200 Subject: [PATCH 66/84] f-15: code add code for binary Signed-off-by: GnomeZworc --- cmd/dhcp/main.go | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 cmd/dhcp/main.go diff --git a/cmd/dhcp/main.go b/cmd/dhcp/main.go new file mode 100644 index 0000000..b2ce08d --- /dev/null +++ b/cmd/dhcp/main.go @@ -0,0 +1,64 @@ +package main + +import ( + "flag" + "fmt" + "net" + "os" + + "git.g3e.fr/syonad/two/internal/dhcp" + "git.g3e.fr/syonad/two/pkg/systemd" +) + +func main() { + subnet := flag.String("subnet", "", "Subnet CIDR (e.g. 10.10.10.0/24)") + name := flag.String("name", "", "Config name (e.g. vpc1_br-00002)") + gateway := flag.String("gateway", "", "Gateway IP (e.g. 10.10.10.1)") + confDir := flag.String("confdir", "/etc/dnsmasq.d", "dnsmasq config directory") + flag.Parse() + + if *subnet == "" || *name == "" || *gateway == "" { + flag.Usage() + os.Exit(1) + } + + _, network, err := net.ParseCIDR(*subnet) + if err != nil { + fmt.Fprintf(os.Stderr, "invalid subnet: %v\n", err) + os.Exit(1) + } + + gw := net.ParseIP(*gateway) + if gw == nil { + fmt.Fprintf(os.Stderr, "invalid gateway IP: %q\n", *gateway) + os.Exit(1) + } + + conf := dhcp.Config{ + Network: network, + Gateway: gw, + Name: *name, + ConfDir: *confDir, + } + + confPath, err := dhcp.GenerateConfig(conf) + if err != nil { + fmt.Fprintf(os.Stderr, "error generating config: %v\n", err) + os.Exit(1) + } + fmt.Printf("dnsmasq config written to %s\n", confPath) + + svc, err := systemd.New() + if err != nil { + fmt.Fprintf(os.Stderr, "error connecting to systemd: %v\n", err) + os.Exit(1) + } + defer svc.Close() + + unit := "dnsmasq@" + *name + ".service" + if err := svc.Start(unit); err != nil { + fmt.Fprintf(os.Stderr, "error starting %s: %v\n", unit, err) + os.Exit(1) + } + fmt.Printf("started %s\n", unit) +} From cfbac0034b5ab9e73b975d55282a58d142d6f8f5 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Mon, 30 Mar 2026 23:20:24 +0200 Subject: [PATCH 67/84] f-15: ci: add dhcp binary build Signed-off-by: GnomeZworc --- .forgejo/workflows/prerelease.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.forgejo/workflows/prerelease.yml b/.forgejo/workflows/prerelease.yml index f20ca66..8b2fe2d 100644 --- a/.forgejo/workflows/prerelease.yml +++ b/.forgejo/workflows/prerelease.yml @@ -38,6 +38,7 @@ jobs: - metacli - agent - vpc + - dhcp uses: ./.forgejo/workflows/build.yml with: tag: ${{ needs.set-release-target.outputs.release_cible }} @@ -51,4 +52,4 @@ jobs: uses: ./.forgejo/workflows/release.yml with: tag: ${{ needs.set-release-target.outputs.release_cible }} - secrets: inherit \ No newline at end of file + secrets: inherit From 02e558e0e2cc59b7768117ef299f507cc5d7bf64 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Mon, 30 Mar 2026 23:20:51 +0200 Subject: [PATCH 68/84] f-15: systemd: add systemd unit for dhcp Signed-off-by: GnomeZworc --- systemd/dnsmasq@.service | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 systemd/dnsmasq@.service diff --git a/systemd/dnsmasq@.service b/systemd/dnsmasq@.service new file mode 100644 index 0000000..0f9d72e --- /dev/null +++ b/systemd/dnsmasq@.service @@ -0,0 +1,11 @@ +[Unit] +Description=dnsmasq in netns %i +After=network.target + +[Service] +Type=simple +ExecStart=/opt/two/bin/run-dnsmasq-in-netns.sh %i +ExecStopPost=/bin/rm -f /run/dnsmasq-%i.pid + +[Install] +WantedBy=multi-user.target From deac1afe9fd1230dfa1d3a598ec54c2dcb47db78 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Mon, 30 Mar 2026 23:21:27 +0200 Subject: [PATCH 69/84] f-15: script: add script for running dhcp in netns Signed-off-by: GnomeZworc --- scripts/run-dnsmasq-in-netns.sh | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 scripts/run-dnsmasq-in-netns.sh diff --git a/scripts/run-dnsmasq-in-netns.sh b/scripts/run-dnsmasq-in-netns.sh new file mode 100644 index 0000000..c0f9253 --- /dev/null +++ b/scripts/run-dnsmasq-in-netns.sh @@ -0,0 +1,21 @@ +#!/bin/bash +set -e + +# Expects one argument: netns_bridge (e.g. vpc-00003_br-00002 or vpc1_br0) +arg="$1" +NETNS="${arg%%_*}" +BRIDGE="${arg#*_}" + +echo "start dnsmasq ${NETNS} ${BRIDGE}" + +exec ip netns exec "${NETNS}" \ + dnsmasq \ + --no-daemon \ + --interface="${BRIDGE}" \ + --bind-interfaces \ + --pid-file="/run/dnsmasq-$arg.pid" \ + --conf-file="/etc/dnsmasq.d/$arg.conf" \ + --no-hosts \ + --no-resolv \ + --log-facility="/var/log/dnsmasq-$arg.log" \ + --no-daemon -p0 From 3288a2a413e35379185e80d992564117ac5e1a5a Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Mon, 30 Mar 2026 23:30:18 +0200 Subject: [PATCH 70/84] f-15: ci: add upload scripts Signed-off-by: GnomeZworc --- .forgejo/workflows/prerelease.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.forgejo/workflows/prerelease.yml b/.forgejo/workflows/prerelease.yml index 8b2fe2d..f9b8a43 100644 --- a/.forgejo/workflows/prerelease.yml +++ b/.forgejo/workflows/prerelease.yml @@ -46,6 +46,24 @@ jobs: goarch: ${{ matrix.goarch }} binari: ${{ matrix.binaries }} secrets: inherit + upload-scripts: + runs-on: docker + needs: [set-release-target] + strategy: + matrix: + script: + - run-dnsmasq-in-netns.sh + steps: + - uses: actions/checkout@v3 + - name: Move asset + run: | + mkdir -p "dist" + cp scripts/${{ matrix.script }} dist/ + - name: Upload script + uses: actions/upload-artifact@v3 + with: + name: ${{ matrix.script }}-${{ needs.set-release-target.outputs.release_cible }} + path: dist/${{ matrix.script }} prerelease: runs-on: docker needs: [set-release-target, build] From c7d20b412429b7976b5f9257a3b8e4bdc6d2bbe1 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Tue, 31 Mar 2026 20:03:52 +0200 Subject: [PATCH 71/84] f-19: test: ajouter des test Signed-off-by: GnomeZworc --- internal/config/agent/config_test.go | 83 +++++++++++ internal/dhcp/dhcp_test.go | 157 +++++++++++++++++++++ internal/metadata/metadata_test.go | 200 +++++++++++++++++++++++++++ pkg/db/kv/kv_test.go | 153 ++++++++++++++++++++ 4 files changed, 593 insertions(+) create mode 100644 internal/config/agent/config_test.go create mode 100644 internal/dhcp/dhcp_test.go create mode 100644 internal/metadata/metadata_test.go create mode 100644 pkg/db/kv/kv_test.go diff --git a/internal/config/agent/config_test.go b/internal/config/agent/config_test.go new file mode 100644 index 0000000..d0f3d31 --- /dev/null +++ b/internal/config/agent/config_test.go @@ -0,0 +1,83 @@ +package configuration + +import ( + "os" + "path/filepath" + "testing" +) + +func writeYAML(t *testing.T, content string) string { + t.Helper() + path := filepath.Join(t.TempDir(), "config.yml") + if err := os.WriteFile(path, []byte(content), 0644); err != nil { + t.Fatalf("impossible d'écrire le fichier de config : %v", err) + } + return path +} + +// --- LoadConfig --- + +func TestLoadConfig_ValidFile(t *testing.T) { + path := writeYAML(t, ` +database: + path: /tmp/mydb +`) + cfg, err := LoadConfig(path) + if err != nil { + t.Fatalf("LoadConfig a échoué : %v", err) + } + if cfg.Database.Path != "/tmp/mydb" { + t.Errorf("database.path attendu %q, obtenu %q", "/tmp/mydb", cfg.Database.Path) + } +} + +func TestLoadConfig_DefaultPath(t *testing.T) { + // Fichier vide → viper applique la valeur par défaut + path := writeYAML(t, "") + cfg, err := LoadConfig(path) + if err != nil { + t.Fatalf("LoadConfig a échoué : %v", err) + } + if cfg.Database.Path != "/var/lib/two/data/" { + t.Errorf("valeur par défaut attendue %q, obtenu %q", "/var/lib/two/data/", cfg.Database.Path) + } +} + +func TestLoadConfig_MissingFile_UsesDefaults(t *testing.T) { + // Fichier inexistant : viper ignore l'erreur ReadInConfig et retourne les défauts + cfg, err := LoadConfig("/chemin/inexistant/config.yml") + if err != nil { + t.Fatalf("LoadConfig devrait retourner les défauts si le fichier est absent : %v", err) + } + if cfg.Database.Path != "/var/lib/two/data/" { + t.Errorf("valeur par défaut attendue, obtenu %q", cfg.Database.Path) + } +} + +func TestLoadConfig_PartialConfig_MissingDatabaseKey(t *testing.T) { + // Fichier sans la clé database → valeur par défaut + path := writeYAML(t, ` +autrekey: valeur +`) + cfg, err := LoadConfig(path) + if err != nil { + t.Fatalf("LoadConfig a échoué : %v", err) + } + if cfg.Database.Path != "/var/lib/two/data/" { + t.Errorf("valeur par défaut attendue, obtenu %q", cfg.Database.Path) + } +} + +func TestLoadConfig_CustomPath(t *testing.T) { + path := writeYAML(t, ` +database: + path: /opt/two/data +`) + cfg, err := LoadConfig(path) + if err != nil { + t.Fatalf("LoadConfig a échoué : %v", err) + } + if cfg.Database.Path != "/opt/two/data" { + t.Errorf("attendu %q, obtenu %q", "/opt/two/data", cfg.Database.Path) + } +} diff --git a/internal/dhcp/dhcp_test.go b/internal/dhcp/dhcp_test.go new file mode 100644 index 0000000..c4a659f --- /dev/null +++ b/internal/dhcp/dhcp_test.go @@ -0,0 +1,157 @@ +package dhcp + +import ( + "net" + "os" + "path/filepath" + "strings" + "testing" +) + +func parseNet(t *testing.T, cidr string) *net.IPNet { + t.Helper() + _, network, err := net.ParseCIDR(cidr) + if err != nil { + t.Fatalf("ParseCIDR(%q) : %v", cidr, err) + } + return network +} + +// --- cloneIP --- + +func TestCloneIP_IsIndependent(t *testing.T) { + ip := net.ParseIP("10.0.0.1").To4() + clone := cloneIP(ip) + clone[3] = 99 + if ip[3] == 99 { + t.Error("cloneIP devrait retourner une copie indépendante") + } +} + +// --- incrementIP --- + +func TestIncrementIP_Simple(t *testing.T) { + ip := net.ParseIP("10.0.0.1").To4() + incrementIP(ip) + if ip.String() != "10.0.0.2" { + t.Errorf("attendu 10.0.0.2, obtenu %s", ip) + } +} + +func TestIncrementIP_Carry(t *testing.T) { + ip := net.ParseIP("10.0.0.255").To4() + incrementIP(ip) + if ip.String() != "10.0.1.0" { + t.Errorf("attendu 10.0.1.0, obtenu %s", ip) + } +} + +// --- GenerateConfig --- + +func newConf(t *testing.T, cidr string) Config { + t.Helper() + _, network, _ := net.ParseCIDR(cidr) + return Config{ + Network: network, + Gateway: net.ParseIP("192.168.1.1").To4(), + Name: "test", + ConfDir: t.TempDir(), + } +} + +func TestGenerateConfig_CreatesFile(t *testing.T) { + conf := newConf(t, "192.168.1.0/29") // 6 hôtes + path, err := GenerateConfig(conf) + if err != nil { + t.Fatalf("GenerateConfig a échoué : %v", err) + } + + if _, err := os.Stat(path); os.IsNotExist(err) { + t.Errorf("le fichier %q n'a pas été créé", path) + } +} + +func TestGenerateConfig_FilenameMatchesName(t *testing.T) { + conf := newConf(t, "192.168.1.0/29") + path, err := GenerateConfig(conf) + if err != nil { + t.Fatalf("GenerateConfig a échoué : %v", err) + } + + expected := filepath.Join(conf.ConfDir, "test.conf") + if path != expected { + t.Errorf("chemin attendu %q, obtenu %q", expected, path) + } +} + +func TestGenerateConfig_ContainsGateway(t *testing.T) { + conf := newConf(t, "192.168.1.0/29") + path, _ := GenerateConfig(conf) + content, _ := os.ReadFile(path) + + if !strings.Contains(string(content), "dhcp-option=3,192.168.1.1") { + t.Errorf("gateway absente du fichier généré :\n%s", content) + } +} + +func TestGenerateConfig_ContainsDhcpRange(t *testing.T) { + _, network, _ := net.ParseCIDR("10.10.0.0/24") + conf := Config{ + Network: network, + Gateway: net.ParseIP("10.10.0.1").To4(), + Name: "vpc1", + ConfDir: t.TempDir(), + } + path, _ := GenerateConfig(conf) + content, _ := os.ReadFile(path) + + if !strings.Contains(string(content), "dhcp-range=10.10.0.0,static,255.255.255.0,12h") { + t.Errorf("dhcp-range absent ou incorrect :\n%s", content) + } +} + +func TestGenerateConfig_OneHostEntryPerIP(t *testing.T) { + // /29 = réseau + broadcast + 6 hôtes → 8 adresses + conf := newConf(t, "10.0.0.0/29") + path, _ := GenerateConfig(conf) + content, _ := os.ReadFile(path) + + lines := strings.Split(string(content), "\n") + count := 0 + for _, l := range lines { + if strings.HasPrefix(l, "dhcp-host=") { + count++ + } + } + // /29 contient 8 adresses (0 à 7) + if count != 8 { + t.Errorf("attendu 8 entrées dhcp-host, obtenu %d", count) + } +} + +func TestGenerateConfig_MACPrefix(t *testing.T) { + conf := newConf(t, "10.0.0.0/30") // 4 adresses + path, _ := GenerateConfig(conf) + content, _ := os.ReadFile(path) + + if !strings.Contains(string(content), "00:22:33:") { + t.Errorf("préfixe MAC 00:22:33: absent :\n%s", content) + } +} + +func TestGenerateConfig_CreatesConfDir(t *testing.T) { + dir := filepath.Join(t.TempDir(), "sous", "dossier") + _, network, _ := net.ParseCIDR("10.0.0.0/30") + conf := Config{ + Network: network, + Gateway: net.ParseIP("10.0.0.1").To4(), + Name: "net", + ConfDir: dir, + } + if _, err := GenerateConfig(conf); err != nil { + t.Fatalf("GenerateConfig devrait créer les répertoires manquants : %v", err) + } + if _, err := os.Stat(dir); os.IsNotExist(err) { + t.Errorf("répertoire %q non créé", dir) + } +} diff --git a/internal/metadata/metadata_test.go b/internal/metadata/metadata_test.go new file mode 100644 index 0000000..62a2830 --- /dev/null +++ b/internal/metadata/metadata_test.go @@ -0,0 +1,200 @@ +package metadata + +import ( + "strings" + "testing" + + "git.g3e.fr/syonad/two/pkg/db/kv" +) + +func newCfg() NoCloudConfig { + return NoCloudConfig{ + VpcName: "vpc-test", + BindIP: "169.254.169.254", + BindPort: "80", + Name: "vm1", + Password: "s3cr3t", + SSHKEY: "ssh-ed25519 AAAA... user@host", + } +} + +func newTestDB(t *testing.T) interface{ Close() error } { + t.Helper() + db := kv.InitDB(kv.Config{Path: t.TempDir()}, false) + t.Cleanup(func() { db.Close() }) + return db +} + +// --- RenderConfig --- + +func TestRenderConfig_MetaData(t *testing.T) { + cfg := newCfg() + out, err := RenderConfig("templates/meta-data.tmpl", cfg) + if err != nil { + t.Fatalf("RenderConfig meta-data : %v", err) + } + if !strings.Contains(out, "instance-id: vm1") { + t.Errorf("instance-id absent :\n%s", out) + } + if !strings.Contains(out, "local-hostname: vm1") { + t.Errorf("local-hostname absent :\n%s", out) + } +} + +func TestRenderConfig_VendorData_ContainsPassword(t *testing.T) { + cfg := newCfg() + out, err := RenderConfig("templates/vendor-data.tmpl", cfg) + if err != nil { + t.Fatalf("RenderConfig vendor-data : %v", err) + } + if !strings.Contains(out, "s3cr3t") { + t.Errorf("password absent du vendor-data :\n%s", out) + } +} + +func TestRenderConfig_VendorData_ContainsSSHKey(t *testing.T) { + cfg := newCfg() + out, err := RenderConfig("templates/vendor-data.tmpl", cfg) + if err != nil { + t.Fatalf("RenderConfig vendor-data : %v", err) + } + if !strings.Contains(out, "ssh-ed25519 AAAA... user@host") { + t.Errorf("clé SSH absente du vendor-data :\n%s", out) + } +} + +func TestRenderConfig_NetworkConfig(t *testing.T) { + cfg := newCfg() + out, err := RenderConfig("templates/network-config.tmpl", cfg) + if err != nil { + t.Fatalf("RenderConfig network-config : %v", err) + } + if !strings.Contains(out, "dhcp4: true") { + t.Errorf("dhcp4 absent du network-config :\n%s", out) + } +} + +func TestRenderConfig_UserData(t *testing.T) { + cfg := newCfg() + out, err := RenderConfig("templates/user-data.tmpl", cfg) + if err != nil { + t.Fatalf("RenderConfig user-data : %v", err) + } + if !strings.Contains(out, "passwd -d root") { + t.Errorf("user-data inattendu :\n%s", out) + } +} + +func TestRenderConfig_InvalidTemplate(t *testing.T) { + _, err := RenderConfig("templates/inexistant.tmpl", newCfg()) + if err == nil { + t.Error("RenderConfig devrait retourner une erreur pour un template inexistant") + } +} + +func TestRenderConfig_SpecialCharsInName(t *testing.T) { + cfg := newCfg() + cfg.Name = "vm-prod-01" + out, err := RenderConfig("templates/meta-data.tmpl", cfg) + if err != nil { + t.Fatalf("RenderConfig : %v", err) + } + if !strings.Contains(out, "vm-prod-01") { + t.Errorf("nom vm-prod-01 absent :\n%s", out) + } +} + +// --- LoadNcCloudInDB / UnLoadNoCloudInDB --- + +func TestLoadNcCloudInDB_StoresAllKeys(t *testing.T) { + db := kv.InitDB(kv.Config{Path: t.TempDir()}, false) + t.Cleanup(func() { db.Close() }) + + cfg := newCfg() + LoadNcCloudInDB(cfg, db) + + keys := []string{ + "metadata/vm1/meta-data", + "metadata/vm1/user-data", + "metadata/vm1/network-config", + "metadata/vm1/vendor-data", + "metadata/vm1/vpc", + "metadata/vm1/bind_ip", + "metadata/vm1/bind_port", + } + for _, key := range keys { + val, err := kv.GetFromDB(db, key) + if err != nil { + t.Errorf("clé %q absente après LoadNcCloudInDB : %v", key, err) + } + if val == "" && key != "metadata/vm1/user-data" { + t.Errorf("clé %q vide après LoadNcCloudInDB", key) + } + } +} + +func TestLoadNcCloudInDB_VpcAndBindValues(t *testing.T) { + db := kv.InitDB(kv.Config{Path: t.TempDir()}, false) + t.Cleanup(func() { db.Close() }) + + cfg := newCfg() + LoadNcCloudInDB(cfg, db) + + vpc, _ := kv.GetFromDB(db, "metadata/vm1/vpc") + if vpc != "vpc-test" { + t.Errorf("vpc attendu %q, obtenu %q", "vpc-test", vpc) + } + + ip, _ := kv.GetFromDB(db, "metadata/vm1/bind_ip") + if ip != "169.254.169.254" { + t.Errorf("bind_ip attendu %q, obtenu %q", "169.254.169.254", ip) + } + + port, _ := kv.GetFromDB(db, "metadata/vm1/bind_port") + if port != "80" { + t.Errorf("bind_port attendu %q, obtenu %q", "80", port) + } +} + +func TestUnLoadNoCloudInDB_RemovesAllKeys(t *testing.T) { + db := kv.InitDB(kv.Config{Path: t.TempDir()}, false) + t.Cleanup(func() { db.Close() }) + + cfg := newCfg() + LoadNcCloudInDB(cfg, db) + UnLoadNoCloudInDB("vm1", db) + + keys := []string{ + "metadata/vm1/meta-data", + "metadata/vm1/user-data", + "metadata/vm1/network-config", + "metadata/vm1/vendor-data", + "metadata/vm1/vpc", + "metadata/vm1/bind_ip", + "metadata/vm1/bind_port", + } + for _, key := range keys { + _, err := kv.GetFromDB(db, key) + if err == nil { + t.Errorf("clé %q devrait être supprimée après UnLoadNoCloudInDB", key) + } + } +} + +func TestUnLoadNoCloudInDB_DoesNotAffectOtherVMs(t *testing.T) { + db := kv.InitDB(kv.Config{Path: t.TempDir()}, false) + t.Cleanup(func() { db.Close() }) + + cfg1 := newCfg() + cfg2 := newCfg() + cfg2.Name = "vm2" + LoadNcCloudInDB(cfg1, db) + LoadNcCloudInDB(cfg2, db) + + UnLoadNoCloudInDB("vm1", db) + + _, err := kv.GetFromDB(db, "metadata/vm2/vpc") + if err != nil { + t.Errorf("vm2 ne devrait pas être supprimée : %v", err) + } +} diff --git a/pkg/db/kv/kv_test.go b/pkg/db/kv/kv_test.go new file mode 100644 index 0000000..de4fd68 --- /dev/null +++ b/pkg/db/kv/kv_test.go @@ -0,0 +1,153 @@ +package kv + +import ( + "errors" + "testing" + + "github.com/dgraph-io/badger/v4" +) + +// newTestDB ouvre une base BadgerDB dans un répertoire temporaire. +// La base est fermée automatiquement en fin de test. +func newTestDB(t *testing.T) *badger.DB { + t.Helper() + db := InitDB(Config{Path: t.TempDir()}, false) + t.Cleanup(func() { db.Close() }) + return db +} + +// --- InitDB --- + +func TestInitDB_ValidPath(t *testing.T) { + db := newTestDB(t) + if db == nil { + t.Fatal("InitDB devrait retourner une DB non-nil") + } +} + +func TestInitDB_InvalidPath_Panics(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Fatal("InitDB avec un chemin invalide devrait paniquer") + } + }() + InitDB(Config{Path: "/chemin/inexistant/absolu"}, false) +} + +// --- AddInDB --- + +func TestAddInDB_NewKey(t *testing.T) { + db := newTestDB(t) + if err := AddInDB(db, "vpc/test", "valeur"); err != nil { + t.Fatalf("AddInDB a échoué : %v", err) + } +} + +func TestAddInDB_OverwriteExistingKey(t *testing.T) { + db := newTestDB(t) + AddInDB(db, "vpc/test", "premiere") + if err := AddInDB(db, "vpc/test", "deuxieme"); err != nil { + t.Fatalf("AddInDB (écrasement) a échoué : %v", err) + } + + val, _ := GetFromDB(db, "vpc/test") + if val != "deuxieme" { + t.Errorf("valeur attendue %q, obtenu %q", "deuxieme", val) + } +} + +// --- GetFromDB --- + +func TestGetFromDB_ExistingKey(t *testing.T) { + db := newTestDB(t) + AddInDB(db, "vpc/foo", "bar") + + val, err := GetFromDB(db, "vpc/foo") + if err != nil { + t.Fatalf("GetFromDB a échoué : %v", err) + } + if val != "bar" { + t.Errorf("valeur attendue %q, obtenu %q", "bar", val) + } +} + +func TestGetFromDB_MissingKey(t *testing.T) { + db := newTestDB(t) + + _, err := GetFromDB(db, "inexistant") + if !errors.Is(err, badger.ErrKeyNotFound) { + t.Errorf("erreur attendue ErrKeyNotFound, obtenu : %v", err) + } +} + +func TestGetFromDB_EmptyValue(t *testing.T) { + db := newTestDB(t) + AddInDB(db, "vpc/vide", "") + + val, err := GetFromDB(db, "vpc/vide") + if err != nil { + t.Fatalf("GetFromDB a échoué : %v", err) + } + if val != "" { + t.Errorf("valeur attendue vide, obtenu %q", val) + } +} + +// --- DeleteInDB --- + +func TestDeleteInDB_SimpleKey(t *testing.T) { + db := newTestDB(t) + AddInDB(db, "vpc/a", "v") + + if err := DeleteInDB(db, "vpc/a"); err != nil { + t.Fatalf("DeleteInDB a échoué : %v", err) + } + + _, err := GetFromDB(db, "vpc/a") + if !errors.Is(err, badger.ErrKeyNotFound) { + t.Errorf("la clé devrait être supprimée, obtenu : %v", err) + } +} + +func TestDeleteInDB_WithSubkeys(t *testing.T) { + db := newTestDB(t) + // Clé parente + sous-clés (préfixe "vpc/net1/") + AddInDB(db, "vpc/net1", "parent") + AddInDB(db, "vpc/net1/ip", "10.0.0.1") + AddInDB(db, "vpc/net1/gw", "10.0.0.254") + + if err := DeleteInDB(db, "vpc/net1"); err != nil { + t.Fatalf("DeleteInDB a échoué : %v", err) + } + + for _, key := range []string{"vpc/net1", "vpc/net1/ip", "vpc/net1/gw"} { + _, err := GetFromDB(db, key) + if !errors.Is(err, badger.ErrKeyNotFound) { + t.Errorf("clé %q devrait être supprimée, obtenu : %v", key, err) + } + } +} + +func TestDeleteInDB_DoesNotDeleteSiblings(t *testing.T) { + db := newTestDB(t) + AddInDB(db, "vpc/net1", "a") + AddInDB(db, "vpc/net2", "b") // ne doit pas être supprimée + + DeleteInDB(db, "vpc/net1") + + val, err := GetFromDB(db, "vpc/net2") + if err != nil { + t.Fatalf("vpc/net2 ne devrait pas être supprimée : %v", err) + } + if val != "b" { + t.Errorf("valeur attendue %q, obtenu %q", "b", val) + } +} + +func TestDeleteInDB_MissingKey(t *testing.T) { + db := newTestDB(t) + // Supprimer une clé inexistante ne doit pas crasher + if err := DeleteInDB(db, "inexistant"); err != nil { + t.Logf("DeleteInDB clé inexistante retourne : %v (non bloquant)", err) + } +} From 1e7575bc7dd6bf87702fc15694ca56bc2797913d Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Tue, 31 Mar 2026 22:04:12 +0200 Subject: [PATCH 72/84] f-15: fix: fix variable Signed-off-by: GnomeZworc --- internal/netns/call_other.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/netns/call_other.go b/internal/netns/call_other.go index 14a8924..1b5794e 100644 --- a/internal/netns/call_other.go +++ b/internal/netns/call_other.go @@ -2,6 +2,6 @@ package netns -func call(name string, fn func() error) error { +func call(_ string, fn func() error) error { return fn() } From 049cabc4899156a03fcec14fabafae9f38ff78ac Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Tue, 31 Mar 2026 22:04:37 +0200 Subject: [PATCH 73/84] f-15: fix: error in thread Signed-off-by: GnomeZworc --- internal/netns/create_linux.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/netns/create_linux.go b/internal/netns/create_linux.go index 8ee0afa..8eb9b65 100644 --- a/internal/netns/create_linux.go +++ b/internal/netns/create_linux.go @@ -4,11 +4,15 @@ package netns import ( "os" + "runtime" "golang.org/x/sys/unix" ) func create(name string) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + base := "/var/run/netns" path := base + "/" + name From beed163b02a422012f83b7c8f931dfaa060b634f Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Tue, 31 Mar 2026 22:05:23 +0200 Subject: [PATCH 74/84] f-14: code: rename veth interface Signed-off-by: GnomeZworc --- internal/vpc/create.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/vpc/create.go b/internal/vpc/create.go index 3c3ad87..b0bf08a 100644 --- a/internal/vpc/create.go +++ b/internal/vpc/create.go @@ -22,7 +22,7 @@ func CreateVPC(db *badger.DB, name string) error { } // create veth public for this netns - if err := netif.CreateVethToNetns("veth"+name+"ext", "vethpublicint", "/var/run/netns/"+name, 9000); err != nil { + if err := netif.CreateVethToNetns("vp-"+name+"-e", "vp-public-i", "/var/run/netns/"+name, 9000); err != nil { return err } @@ -34,24 +34,24 @@ func CreateVPC(db *badger.DB, name string) error { } // set veth to ext public bridge - if err := netif.BridgeSetMaster("veth"+name+"ext", "br-public"); err != nil { + if err := netif.BridgeSetMaster("vp-"+name+"-e", "br-public"); err != nil { return err } // set veth to int public bridge if err := netns.Call(name, func() error { - return netif.BridgeSetMaster("vethpublicint", "br-public") + return netif.BridgeSetMaster("vp-public-i", "br-public") }); err != nil { return err } // set set ext veth up - if err := netif.LinkSetUp("veth" + name + "ext"); err != nil { + if err := netif.LinkSetUp("vp-" + name + "-e"); err != nil { return nil } // set set int veth up if err := netns.Call(name, func() error { - return netif.LinkSetUp("vethpublicint") + return netif.LinkSetUp("vp-public-i") }); err != nil { return err } From 74d7fc1425923b95c0b9142bc934a8590e48c074 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Thu, 2 Apr 2026 23:49:48 +0200 Subject: [PATCH 75/84] f-15: fix: error in netns creation Signed-off-by: GnomeZworc --- internal/netns/create_linux.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/internal/netns/create_linux.go b/internal/netns/create_linux.go index 8eb9b65..51c739a 100644 --- a/internal/netns/create_linux.go +++ b/internal/netns/create_linux.go @@ -3,6 +3,7 @@ package netns import ( + "fmt" "os" "runtime" @@ -20,6 +21,12 @@ func create(name string) error { return err } + // si le fichier existe déjà, le démonter d'abord + if _, err := os.Stat(path); err == nil { + unix.Unmount(path, unix.MNT_DETACH) + os.Remove(path) + } + // fichier cible f, err := os.Create(path) if err != nil { @@ -39,9 +46,12 @@ func create(name string) error { return err } - // bind mount du netns courant vers /var/run/netns/ + // bind mount du netns du thread courant vers /var/run/netns/ + // /proc/self/ns/net pointe vers le ns du processus (thread principal), + // pas du thread courant — il faut utiliser le tid explicitement + threadNsPath := fmt.Sprintf("/proc/self/task/%d/ns/net", unix.Gettid()) if err := unix.Mount( - "/proc/self/ns/net", + threadNsPath, path, "", unix.MS_BIND, From d9f2ec4e67c48c08acae2f53b7e16e960bf7f21b Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Thu, 2 Apr 2026 23:50:22 +0200 Subject: [PATCH 76/84] f-14: type: fix return error Signed-off-by: GnomeZworc --- internal/vpc/create.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/vpc/create.go b/internal/vpc/create.go index b0bf08a..a6e6aaa 100644 --- a/internal/vpc/create.go +++ b/internal/vpc/create.go @@ -47,7 +47,7 @@ func CreateVPC(db *badger.DB, name string) error { // set set ext veth up if err := netif.LinkSetUp("vp-" + name + "-e"); err != nil { - return nil + return err } // set set int veth up if err := netns.Call(name, func() error { From ff17645c625526897dc83c6a38d4a8e718bdeadf Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Thu, 2 Apr 2026 23:50:46 +0200 Subject: [PATCH 77/84] f-14: fix: rename interface in delete vpc Signed-off-by: GnomeZworc --- internal/vpc/delete.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/vpc/delete.go b/internal/vpc/delete.go index a05e807..10c863d 100644 --- a/internal/vpc/delete.go +++ b/internal/vpc/delete.go @@ -12,7 +12,7 @@ func DeleteVPC(db *badger.DB, name string) error { if state, err := kv.GetFromDB(db, "vpc/"+name+"/state"); err != nil { return err } else if state == "deleting" { - if err := netif.DeleteLink(name + "-ext"); err != nil { + if err := netif.DeleteLink("vp-" + name + "-e"); err != nil { return err } From 40b29657ae1f1638a15fdefd3e09eafc33f9182d Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Mon, 6 Apr 2026 18:24:23 +0200 Subject: [PATCH 78/84] f-15: fix: error in manage systemd Signed-off-by: GnomeZworc --- pkg/systemd/main.go | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/pkg/systemd/main.go b/pkg/systemd/main.go index ea5fef0..e89af6e 100644 --- a/pkg/systemd/main.go +++ b/pkg/systemd/main.go @@ -11,6 +11,7 @@ import ( const ( defaultTimeout = 5 * time.Second + jobTimeout = 30 * time.Second jobMode = "replace" ) @@ -28,10 +29,7 @@ type ServiceStatus struct { // New crée une connexion D-Bus systemd (scope système) func New() (*Manager, error) { - ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) - defer cancel() - - conn, err := dbus.NewSystemConnectionContext(ctx) + conn, err := dbus.NewSystemConnectionContext(context.Background()) if err != nil { return nil, err } @@ -57,17 +55,17 @@ func (m *Manager) Stop(service string) error { } func (m *Manager) job(method, service string) error { - ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) - defer cancel() + callCtx, callCancel := context.WithTimeout(context.Background(), defaultTimeout) + defer callCancel() ch := make(chan string, 1) var err error switch method { case "StartUnit": - _, err = m.conn.StartUnitContext(ctx, service, jobMode, ch) + _, err = m.conn.StartUnitContext(callCtx, service, jobMode, ch) case "StopUnit": - _, err = m.conn.StopUnitContext(ctx, service, jobMode, ch) + _, err = m.conn.StopUnitContext(callCtx, service, jobMode, ch) default: return errors.New("unsupported job method") } @@ -76,9 +74,16 @@ func (m *Manager) job(method, service string) error { return err } - result := <-ch - if result != "done" { - return fmt.Errorf("%s %s failed: %s", method, service, result) + waitCtx, waitCancel := context.WithTimeout(context.Background(), jobTimeout) + defer waitCancel() + + select { + case result := <-ch: + if result != "done" { + return fmt.Errorf("%s %s failed: %s", method, service, result) + } + case <-waitCtx.Done(): + return fmt.Errorf("%s %s timed out after %s", method, service, jobTimeout) } return nil From 825fabb64aca77dfd548b553301db54f4d69339e Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Mon, 6 Apr 2026 20:15:50 +0200 Subject: [PATCH 79/84] f-15: error: add error handle Signed-off-by: GnomeZworc --- cmd/metacli/main.go | 10 +++++++--- internal/metadata/handle.go | 26 ++++++++++++++++++++------ 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/cmd/metacli/main.go b/cmd/metacli/main.go index 20706d2..140bde6 100644 --- a/cmd/metacli/main.go +++ b/cmd/metacli/main.go @@ -35,15 +35,19 @@ func main() { defer db.Close() if *start { - metadata.StartMetadata(metadata.NoCloudConfig{ + if err := metadata.StartMetadata(metadata.NoCloudConfig{ VpcName: *vpc, Name: *vm_name, BindIP: *bind_ip, BindPort: *bind_port, Password: *password, SSHKEY: *ssh_key, - }, db, *dryrun) + }, db, *dryrun); err != nil { + fmt.Println(err) + } } else if *stop { - metadata.StopMetadata(*vm_name, db, *dryrun) + if err := metadata.StopMetadata(*vm_name, db, *dryrun); err != nil { + fmt.Println(err) + } } } diff --git a/internal/metadata/handle.go b/internal/metadata/handle.go index 33ac088..9ec5486 100644 --- a/internal/metadata/handle.go +++ b/internal/metadata/handle.go @@ -1,26 +1,40 @@ package metadata import ( + "fmt" + "git.g3e.fr/syonad/two/pkg/systemd" "github.com/dgraph-io/badger/v4" ) -func StartMetadata(config NoCloudConfig, db *badger.DB, dryrun bool) { - service, _ := systemd.New() +func StartMetadata(config NoCloudConfig, db *badger.DB, dryrun bool) error { + service, err := systemd.New() + if err != nil { + return fmt.Errorf("failed to connect to systemd: %w", err) + } defer service.Close() LoadNcCloudInDB(config, db) if !dryrun { - service.Start("metadata@" + config.Name) + if err := service.Start("metadata@" + config.Name + ".service"); err != nil { + return fmt.Errorf("failed to start metadata@%s: %w", config.Name, err) + } } + return nil } -func StopMetadata(vm_name string, db *badger.DB, dryrun bool) { - service, _ := systemd.New() +func StopMetadata(vm_name string, db *badger.DB, dryrun bool) error { + service, err := systemd.New() + if err != nil { + return fmt.Errorf("failed to connect to systemd: %w", err) + } defer service.Close() UnLoadNoCloudInDB(vm_name, db) if !dryrun { - service.Stop("metadata@" + vm_name) + if err := service.Stop("metadata@" + vm_name + ".service"); err != nil { + return fmt.Errorf("failed to stop metadata@%s: %w", vm_name, err) + } } + return nil } From bf00d74a261f6353d2ed924ea38456523f68a873 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Mon, 6 Apr 2026 20:16:24 +0200 Subject: [PATCH 80/84] f-15: fix: remove lock from db in readonly Signed-off-by: GnomeZworc --- pkg/db/kv/init.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/db/kv/init.go b/pkg/db/kv/init.go index cc09cb0..677c656 100644 --- a/pkg/db/kv/init.go +++ b/pkg/db/kv/init.go @@ -6,7 +6,8 @@ import ( func InitDB(conf Config, readonly bool) *badger.DB { opts := badger.DefaultOptions(conf.Path). - WithReadOnly(readonly) + WithReadOnly(readonly). + WithBypassLockGuard(readonly) opts.Logger = nil opts.ValueLogFileSize = 10 << 20 // 10 Mo par fichier vlog opts.NumMemtables = 1 From cad1a9f46b032d18c4481b70ab5cbf7b04546733 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Sat, 11 Apr 2026 23:27:07 +0200 Subject: [PATCH 81/84] f-15: ia: first subnet try Signed-off-by: GnomeZworc --- cmd/subnet/main.go | 93 +++++++++++++++++ internal/netif/addr.go | 21 ++++ internal/netif/route_linux.go | 21 ++++ internal/netif/route_other.go | 9 ++ internal/netif/vxlan.go | 20 ++++ internal/subnet/create.go | 186 ++++++++++++++++++++++++++++++++++ internal/subnet/delete.go | 95 +++++++++++++++++ 7 files changed, 445 insertions(+) create mode 100644 cmd/subnet/main.go create mode 100644 internal/netif/addr.go create mode 100644 internal/netif/route_linux.go create mode 100644 internal/netif/route_other.go create mode 100644 internal/netif/vxlan.go create mode 100644 internal/subnet/create.go create mode 100644 internal/subnet/delete.go diff --git a/cmd/subnet/main.go b/cmd/subnet/main.go new file mode 100644 index 0000000..05ecd47 --- /dev/null +++ b/cmd/subnet/main.go @@ -0,0 +1,93 @@ +package main + +import ( + "flag" + "fmt" + "os" + + configuration "git.g3e.fr/syonad/two/internal/config/agent" + "git.g3e.fr/syonad/two/internal/subnet" + "git.g3e.fr/syonad/two/pkg/db/kv" + + "github.com/dgraph-io/badger/v4" +) + +var ( + name = flag.String("name", "", "Subnet name (ex: sn-00001)") + vpcName = flag.String("vpc", "", "VPC name") + vxlanID = flag.String("vxlan-id", "", "VXLAN ID") + localIP = flag.String("local-ip", "", "Local VTEP IP") + gatewayIP = flag.String("gateway-ip", "", "Gateway IP") + cidr = flag.String("cidr", "", "Subnet CIDR (ex: 10.10.10.0/24)") + action = flag.String("action", "", "Action à effectuer") + conf_file = flag.String("conf", "/etc/two/agent.yml", "Configuration file") +) + +var DB *badger.DB + +func main() { + flag.Parse() + + conf, err := configuration.LoadConfig(*conf_file) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + DB = kv.InitDB(kv.Config{ + Path: conf.Database.Path, + }, false) + defer DB.Close() + + switch *action { + case "create": + if *name == "" || *vpcName == "" || *vxlanID == "" || *localIP == "" || *gatewayIP == "" || *cidr == "" { + fmt.Println("create requires: -name -vpc -vxlan-id -local-ip -gateway-ip -cidr") + os.Exit(1) + } + kv.AddInDB(DB, "subnet/"+*name+"/state", "creating") + kv.AddInDB(DB, "subnet/"+*name+"/vpc", *vpcName) + kv.AddInDB(DB, "subnet/"+*name+"/vxlan_id", *vxlanID) + kv.AddInDB(DB, "subnet/"+*name+"/local_ip", *localIP) + kv.AddInDB(DB, "subnet/"+*name+"/gateway_ip", *gatewayIP) + kv.AddInDB(DB, "subnet/"+*name+"/cidr", *cidr) + if err := subnet.CreateSubnet(DB, *name); err != nil { + fmt.Println(err) + os.Exit(1) + } + + case "delete": + if *name == "" { + fmt.Println("delete requires: -name") + os.Exit(1) + } + kv.AddInDB(DB, "subnet/"+*name+"/state", "deleting") + if err := subnet.DeleteSubnet(DB, *name); err != nil { + fmt.Println(err) + os.Exit(1) + } + if state, err := kv.GetFromDB(DB, "subnet/"+*name+"/state"); err != nil { + fmt.Println(err) + os.Exit(1) + } else if state == "deleted" { + kv.DeleteInDB(DB, "subnet/"+*name) + } + + case "check": + if *name == "" { + fmt.Println("check requires: -name") + os.Exit(1) + } + if state, err := kv.GetFromDB(DB, "subnet/"+*name+"/state"); err != nil { + os.Exit(1) + } else if state != "created" { + os.Exit(1) + } + + default: + fmt.Printf("Available commands:\n - create\n - delete\n - check\n") + os.Exit(1) + } + + os.Exit(0) +} diff --git a/internal/netif/addr.go b/internal/netif/addr.go new file mode 100644 index 0000000..b45b6aa --- /dev/null +++ b/internal/netif/addr.go @@ -0,0 +1,21 @@ +package netif + +import ( + "net" + + "github.com/vishvananda/netlink" +) + +func AddrAdd(iface string, ip net.IP) error { + link, err := netlink.LinkByName(iface) + if err != nil { + return err + } + addr := &netlink.Addr{ + IPNet: &net.IPNet{ + IP: ip, + Mask: net.CIDRMask(32, 32), + }, + } + return netlink.AddrAdd(link, addr) +} diff --git a/internal/netif/route_linux.go b/internal/netif/route_linux.go new file mode 100644 index 0000000..f68d191 --- /dev/null +++ b/internal/netif/route_linux.go @@ -0,0 +1,21 @@ +//go:build linux + +package netif + +import ( + "net" + + "github.com/vishvananda/netlink" +) + +func RouteAdd(iface string, subnet *net.IPNet) error { + link, err := netlink.LinkByName(iface) + if err != nil { + return err + } + return netlink.RouteAdd(&netlink.Route{ + LinkIndex: link.Attrs().Index, + Dst: subnet, + Scope: netlink.SCOPE_LINK, + }) +} diff --git a/internal/netif/route_other.go b/internal/netif/route_other.go new file mode 100644 index 0000000..685c5bf --- /dev/null +++ b/internal/netif/route_other.go @@ -0,0 +1,9 @@ +//go:build !linux + +package netif + +import "net" + +func RouteAdd(_ string, _ *net.IPNet) error { + return nil +} diff --git a/internal/netif/vxlan.go b/internal/netif/vxlan.go new file mode 100644 index 0000000..eacae7c --- /dev/null +++ b/internal/netif/vxlan.go @@ -0,0 +1,20 @@ +package netif + +import ( + "net" + + "github.com/vishvananda/netlink" +) + +func CreateVxlan(name string, vxlanID int, localIP net.IP) error { + vxlan := &netlink.Vxlan{ + LinkAttrs: netlink.LinkAttrs{ + Name: name, + }, + VxlanId: vxlanID, + Port: 4789, + SrcAddr: localIP, + Learning: false, + } + return netlink.LinkAdd(vxlan) +} diff --git a/internal/subnet/create.go b/internal/subnet/create.go new file mode 100644 index 0000000..9bfe02a --- /dev/null +++ b/internal/subnet/create.go @@ -0,0 +1,186 @@ +package subnet + +import ( + "fmt" + "net" + "os/exec" + "strconv" + "strings" + + "git.g3e.fr/syonad/two/internal/dhcp" + "git.g3e.fr/syonad/two/internal/netif" + "git.g3e.fr/syonad/two/internal/netns" + "git.g3e.fr/syonad/two/pkg/db/kv" + "git.g3e.fr/syonad/two/pkg/systemd" + + "github.com/dgraph-io/badger/v4" +) + +func CreateSubnet(db *badger.DB, subnetName string) error { + state, err := kv.GetFromDB(db, "subnet/"+subnetName+"/state") + if err != nil { + return err + } + if state != "creating" { + return nil + } + + // lecture des paramètres depuis la DB + vpcName, err := kv.GetFromDB(db, "subnet/"+subnetName+"/vpc") + if err != nil { + return fmt.Errorf("get vpc: %w", err) + } + + vxlanIDStr, err := kv.GetFromDB(db, "subnet/"+subnetName+"/vxlan_id") + if err != nil { + return fmt.Errorf("get vxlan_id: %w", err) + } + vxlanID, err := strconv.Atoi(vxlanIDStr) + if err != nil { + return fmt.Errorf("parse vxlan_id: %w", err) + } + + localIPStr, err := kv.GetFromDB(db, "subnet/"+subnetName+"/local_ip") + if err != nil { + return fmt.Errorf("get local_ip: %w", err) + } + localIP := net.ParseIP(localIPStr) + if localIP == nil { + return fmt.Errorf("invalid local_ip: %s", localIPStr) + } + + gatewayIPStr, err := kv.GetFromDB(db, "subnet/"+subnetName+"/gateway_ip") + if err != nil { + return fmt.Errorf("get gateway_ip: %w", err) + } + gatewayIP := net.ParseIP(gatewayIPStr) + if gatewayIP == nil { + return fmt.Errorf("invalid gateway_ip: %s", gatewayIPStr) + } + + cidr, err := kv.GetFromDB(db, "subnet/"+subnetName+"/cidr") + if err != nil { + return fmt.Errorf("get cidr: %w", err) + } + _, subnet, err := net.ParseCIDR(cidr) + if err != nil { + return fmt.Errorf("parse cidr: %w", err) + } + + // subnet_id = partie après le premier '-' (ex: "sn-00001" -> "00001") + subnetID := strings.SplitN(subnetName, "-", 2)[1] + bridge := "br-" + subnetID + vxlanIface := fmt.Sprintf("vxlan-%d", vxlanID) + + // veth pair + if err := netif.CreateVethToNetns("v-"+subnetID+"-e", "v-"+subnetID+"-i", "/var/run/netns/"+vpcName, 1500); err != nil { + return fmt.Errorf("create veth: %w", err) + } + + // bridge dans le root netns + if err := netif.CreateBridge(bridge, 1500); err != nil { + return fmt.Errorf("create bridge: %w", err) + } + + // bridge dans le netns VPC + if err := netns.Call(vpcName, func() error { + return netif.CreateBridge(bridge, 1500) + }); err != nil { + return fmt.Errorf("create bridge in netns: %w", err) + } + + // vxlan + if err := netif.CreateVxlan(vxlanIface, vxlanID, localIP); err != nil { + return fmt.Errorf("create vxlan: %w", err) + } + + // ajout des interfaces dans les bridges + if err := netif.BridgeSetMaster("v-"+subnetID+"-e", bridge); err != nil { + return fmt.Errorf("add veth-e to bridge: %w", err) + } + if err := netns.Call(vpcName, func() error { + return netif.BridgeSetMaster("v-"+subnetID+"-i", bridge) + }); err != nil { + return fmt.Errorf("add veth-i to bridge in netns: %w", err) + } + if err := netif.BridgeSetMaster(vxlanIface, bridge); err != nil { + return fmt.Errorf("add vxlan to bridge: %w", err) + } + + // montée des interfaces dans le root netns + for _, iface := range []string{"v-" + subnetID + "-e", vxlanIface, bridge} { + if err := netif.LinkSetUp(iface); err != nil { + return fmt.Errorf("set up %s: %w", iface, err) + } + } + + // montée des interfaces dans le netns VPC + if err := netns.Call(vpcName, func() error { + for _, iface := range []string{"v-" + subnetID + "-i", bridge} { + if err := netif.LinkSetUp(iface); err != nil { + return fmt.Errorf("set up %s: %w", iface, err) + } + } + return nil + }); err != nil { + return fmt.Errorf("set up interfaces in netns: %w", err) + } + + // IP gateway (/32) sur le bridge interne + if err := netns.Call(vpcName, func() error { + return netif.AddrAdd(bridge, gatewayIP) + }); err != nil { + return fmt.Errorf("add addr to bridge in netns: %w", err) + } + + // route subnet (scope link) dans le netns VPC + if err := netns.Call(vpcName, func() error { + return netif.RouteAdd(bridge, subnet) + }); err != nil { + return fmt.Errorf("add route in netns: %w", err) + } + + // ebtables : drop ARP Request vers la gateway sur ce bridge + if err := exec.Command("ebtables", "-A", "FORWARD", + "--out-interface", bridge, + "-p", "arp", + "--arp-op", "Request", + "--arp-ip-dst", gatewayIP.String(), + "-j", "DROP").Run(); err != nil { + return fmt.Errorf("ebtables arp rule: %w", err) + } + + // ebtables : drop trafic DHCP sur ce bridge + if err := exec.Command("ebtables", "-A", "FORWARD", + "--out-interface", bridge, + "-p", "IPv4", + "--ip-protocol", "udp", + "--ip-source-port", "67:68", + "--ip-destination-port", "67:68", + "-j", "DROP").Run(); err != nil { + return fmt.Errorf("ebtables dhcp rule: %w", err) + } + + // génération de la config dnsmasq et démarrage du service + conf := dhcp.Config{ + Network: subnet, + Gateway: gatewayIP, + Name: vpcName + "_" + bridge, + ConfDir: "/etc/dnsmasq.d", + } + if _, err := dhcp.GenerateConfig(conf); err != nil { + return fmt.Errorf("generate dhcp config: %w", err) + } + + svc, err := systemd.New() + if err != nil { + return fmt.Errorf("connect to systemd: %w", err) + } + defer svc.Close() + + if err := svc.Start("dnsmasq@" + conf.Name + ".service"); err != nil { + return fmt.Errorf("start dnsmasq: %w", err) + } + + return kv.AddInDB(db, "subnet/"+subnetName+"/state", "created") +} diff --git a/internal/subnet/delete.go b/internal/subnet/delete.go new file mode 100644 index 0000000..650aa6f --- /dev/null +++ b/internal/subnet/delete.go @@ -0,0 +1,95 @@ +package subnet + +import ( + "fmt" + "os" + "os/exec" + "strings" + + "git.g3e.fr/syonad/two/internal/netif" + "git.g3e.fr/syonad/two/internal/netns" + "git.g3e.fr/syonad/two/pkg/db/kv" + "git.g3e.fr/syonad/two/pkg/systemd" + + "github.com/dgraph-io/badger/v4" +) + +func DeleteSubnet(db *badger.DB, subnetName string) error { + state, err := kv.GetFromDB(db, "subnet/"+subnetName+"/state") + if err != nil { + return err + } + if state != "deleting" { + return nil + } + + vpcName, err := kv.GetFromDB(db, "subnet/"+subnetName+"/vpc") + if err != nil { + return fmt.Errorf("get vpc: %w", err) + } + + vxlanIDStr, err := kv.GetFromDB(db, "subnet/"+subnetName+"/vxlan_id") + if err != nil { + return fmt.Errorf("get vxlan_id: %w", err) + } + + subnetID := strings.SplitN(subnetName, "-", 2)[1] + bridge := "br-" + subnetID + vxlanIface := "vxlan-" + vxlanIDStr + + // arrêt du service dnsmasq + svc, err := systemd.New() + if err != nil { + return fmt.Errorf("connect to systemd: %w", err) + } + defer svc.Close() + + svcName := "dnsmasq@" + vpcName + "_" + bridge + ".service" + if err := svc.Stop(svcName); err != nil { + return fmt.Errorf("stop dnsmasq: %w", err) + } + + // suppression de la config dnsmasq + if err := os.Remove("/etc/dnsmasq.d/" + vpcName + "_" + bridge + ".conf"); err != nil && !os.IsNotExist(err) { + return fmt.Errorf("remove dnsmasq config: %w", err) + } + + // suppression des règles ebtables + exec.Command("ebtables", "-D", "FORWARD", + "--out-interface", bridge, + "-p", "arp", + "--arp-op", "Request", + "-j", "DROP").Run() + + exec.Command("ebtables", "-D", "FORWARD", + "--out-interface", bridge, + "-p", "IPv4", + "--ip-protocol", "udp", + "--ip-source-port", "67:68", + "--ip-destination-port", "67:68", + "-j", "DROP").Run() + + // suppression du bridge dans le netns VPC + if err := netns.Call(vpcName, func() error { + return netif.DeleteLink(bridge) + }); err != nil { + return fmt.Errorf("delete bridge in netns: %w", err) + } + + // suppression du vxlan + if err := netif.DeleteLink(vxlanIface); err != nil { + return fmt.Errorf("delete vxlan: %w", err) + } + + // suppression du veth pair (supprime les deux côtés) + if err := netif.DeleteLink("v-" + subnetID + "-e"); err != nil { + return fmt.Errorf("delete veth: %w", err) + } + + // suppression du bridge dans le root netns + if err := netif.DeleteLink(bridge); err != nil { + return fmt.Errorf("delete bridge: %w", err) + } + + return kv.AddInDB(db, "subnet/"+subnetName+"/state", "deleted") +} From 9409e1f8d21aa488865669a7d2f35cf03d3e08a7 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Sat, 11 Apr 2026 23:29:36 +0200 Subject: [PATCH 82/84] f-15: ci: add subnet bin Signed-off-by: GnomeZworc --- .forgejo/workflows/prerelease.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.forgejo/workflows/prerelease.yml b/.forgejo/workflows/prerelease.yml index f9b8a43..8003e82 100644 --- a/.forgejo/workflows/prerelease.yml +++ b/.forgejo/workflows/prerelease.yml @@ -39,6 +39,7 @@ jobs: - agent - vpc - dhcp + - subnet uses: ./.forgejo/workflows/build.yml with: tag: ${{ needs.set-release-target.outputs.release_cible }} From 141e028e495c0b863c8573c3a4f53c8f529e0bf9 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Tue, 21 Apr 2026 23:19:36 +0200 Subject: [PATCH 83/84] update license Signed-off-by: GnomeZworc --- LICENSE | 229 +++++++++++++++++++++++--------------------------------- 1 file changed, 95 insertions(+), 134 deletions(-) diff --git a/LICENSE b/LICENSE index baee873..1a71cb6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,170 +1,131 @@ -Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International +# PolyForm Noncommercial License 1.0.0 - Creative Commons Corporation (“Creative Commons”) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is” basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. + -Using Creative Commons Public Licenses +## Acceptance -Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. +In order to get any license under these terms, you must agree +to them as both strict obligations and conditions to all +your licenses. -Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. More considerations for licensors. +## Copyright License -Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public. +The licensor grants you a copyright license for the +software to do everything you might do with the software +that would otherwise infringe the licensor's copyright +in it for any permitted purpose. However, you may +only distribute the software according to [Distribution +License](#distribution-license) and make changes or new works +based on the software according to [Changes and New Works +License](#changes-and-new-works-license). -Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License +## Distribution License -By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. +The licensor grants you an additional copyright license +to distribute copies of the software. Your license +to distribute covers distributing the software with +changes and new works permitted by [Changes and New Works +License](#changes-and-new-works-license). -Section 1 – Definitions. +## Notices - a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. +You must ensure that anyone who gets a copy of any part of +the software from you also gets a copy of these terms or the +URL for them above, as well as copies of any plain-text lines +beginning with `Required Notice:` that the licensor provided +with the software. For example: - b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. +> Required Notice: Copyright Yoyodyne, Inc. (http://example.com) - c. BY-NC-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses, approved by Creative Commons as essentially the equivalent of this Public License. +## Changes and New Works License - d. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. +The licensor grants you an additional copyright license to +make changes and new works based on the software for any +permitted purpose. - e. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. +## Patent License - f. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. +The licensor grants you a patent license for the software that +covers patent claims the licensor can license, or becomes able +to license, that you would infringe by using the software. - g. License Elements means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution, NonCommercial, and ShareAlike. +## Noncommercial Purposes - h. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. +Any noncommercial purpose is a permitted purpose. - i. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. +## Personal Uses - j. Licensor means the individual(s) or entity(ies) granting rights under this Public License. +Personal use for research, experiment, and testing for +the benefit of public knowledge, personal study, private +entertainment, hobby projects, amateur pursuits, or religious +observance, without any anticipated commercial application, +is use for a permitted purpose. - k. NonCommercial means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange. +## Noncommercial Organizations - l. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. +Use by any charitable organization, educational institution, +public research organization, public safety or health +organization, environmental protection organization, +or government institution is use for a permitted purpose +regardless of the source of funding or obligations resulting +from the funding. - m. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. +## Fair Use - n. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. +You may have "fair use" rights for the software under the +law. These terms do not limit them. -Section 2 – Scope. +## No Other Rights - a. License grant. +These terms do not allow you to sublicense or transfer any of +your licenses to anyone else, or prevent the licensor from +granting licenses to anyone else. These terms do not imply +any other licenses. - 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: +## Patent Defense - A. reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and +If you make any written claim that the software infringes or +contributes to infringement of any patent, your patent license +for the software granted under these terms ends immediately. If +your company makes such a claim, your patent license ends +immediately for work on behalf of your company. - B. produce, reproduce, and Share Adapted Material for NonCommercial purposes only. +## Violations - 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. +The first time you are notified in writing that you have +violated any of these terms, or done anything with the software +not covered by your licenses, your licenses can nonetheless +continue if you come into full compliance with these terms, +and take practical steps to correct past violations, within +32 days of receiving notice. Otherwise, all your licenses +end immediately. - 3. Term. The term of this Public License is specified in Section 6(a). +## No Liability - 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. +***As far as the law allows, the software comes as is, without +any warranty or condition, and the licensor will not be liable +to you for any damages arising out of these terms or the use +or nature of the software, under any kind of legal claim.*** - 5. Downstream recipients. +## Definitions - A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. +The **licensor** is the individual or entity offering these +terms, and the **software** is the software the licensor makes +available under these terms. - B. Additional offer from the Licensor – Adapted Material. Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter’s License You apply. +**You** refers to the individual or entity agreeing to these +terms. - C. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. +**Your company** is any legal entity, sole proprietorship, +or other kind of organization that you work for, plus all +organizations that have control over, are under the control of, +or are under common control with that organization. **Control** +means ownership of substantially all the assets of an entity, +or the power to direct its management and policies by vote, +contract, or otherwise. Control can be direct or indirect. - 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). +**Your licenses** are all the licenses granted to you for the +software under these terms. - b. Other rights. - - 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. - - 2. Patent and trademark rights are not licensed under this Public License. - - 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes. - -Section 3 – License Conditions. - -Your exercise of the Licensed Rights is expressly made subject to the following conditions. - - a. Attribution. - - 1. If You Share the Licensed Material (including in modified form), You must: - - A. retain the following if it is supplied by the Licensor with the Licensed Material: - - i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); - - ii. a copyright notice; - - iii. a notice that refers to this Public License; - - iv. a notice that refers to the disclaimer of warranties; - - v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; - - B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and - - C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. - - 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. - - 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. - - b. ShareAlike.In addition to the conditions in Section 3(a), if You Share Adapted Material You produce, the following conditions also apply. - - 1. The Adapter’s License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-NC-SA Compatible License. - - 2. You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material. - - 3. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply. - -Section 4 – Sui Generis Database Rights. - -Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: - - a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only; - - b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, including for purposes of Section 3(b); and - - c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. -For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. - -Section 5 – Disclaimer of Warranties and Limitation of Liability. - - a. Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You. - - b. To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You. - - c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. - -Section 6 – Term and Termination. - - a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. - - b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: - - 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or - - 2. upon express reinstatement by the Licensor. - - For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. - - c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. - - d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. - -Section 7 – Other Terms and Conditions. - - a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. - - b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. - -Section 8 – Interpretation. - - a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. - - b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. - - c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. - - d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. - -Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. - -Creative Commons may be contacted at creativecommons.org. +**Use** means anything you do with the software requiring one +of your licenses. From 776ba5e287c1e86c2fc816ad12638b611aa9ebe1 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Wed, 23 Apr 2025 22:13:48 +0200 Subject: [PATCH 84/84] add notice file Signed-off-by: GnomeZworc --- NOTICE.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 NOTICE.md diff --git a/NOTICE.md b/NOTICE.md new file mode 100644 index 0000000..d8b9386 --- /dev/null +++ b/NOTICE.md @@ -0,0 +1,11 @@ +# 🔒 Utilisation de logiciels open source + +Notre logiciel utilise des composants open source, notamment **NetBox**, un outil de gestion d'infrastructure réseau développé par la communauté NetBox. + +NetBox est distribué sous la **licence Apache License, Version 2.0**. Vous pouvez consulter cette licence à l'adresse suivante : +http://www.apache.org/licenses/LICENSE-2.0 + +Aucune modification n’a été apportée à NetBox dans notre intégration. Il est utilisé tel quel, en tant que base de données d'infrastructure. + +Copyright © 2025 NetBox Contributors. +Nous reconnaissons et respectons le travail de la communauté NetBox. \ No newline at end of file