Merge pull request 'start project' (#2) from agent into main

Reviewed-on: #2
This commit is contained in:
nicolas.boufideline 2025-12-18 10:46:04 +00:00 committed by G3E Git Instance
commit fc15b358a0
Signed by: G3E Git Instance
SSH key fingerprint: SHA256:7qPkHsv5cK9DqRLWKVhd6yvG6rpDxbWny9r8CMChJb0
17 changed files with 581 additions and 0 deletions

View file

@ -0,0 +1,43 @@
on:
workflow_call:
inputs:
tag:
required: true
type: string
goos:
required: true
type: string
goarch:
required: true
type: string
binari:
required: true
type: string
jobs:
build:
runs-on: docker
env:
RELEASE_CIBLE: ${{ inputs.tag }}
GOOS: ${{ inputs.goos }}
GOARCH: ${{ inputs.goarch }}
BINARI: ${{ inputs.binari }}
CGO_ENABLED: 0
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v5
with:
go-version: "1.21"
- name: Build du projet
run: |
echo "Building for ${BINARI}/${GOOS}/${GOARCH} (release: ${RELEASE_CIBLE})"
go env GOOS GOARCH
mkdir -p dist/
go build -o dist/${BINARI}_${GOOS}_${GOARCH} ./cmd/${BINARI}
echo "artifact pour ${RELEASE_CIBLE} ${BINARI} ${GOOS} ${GOARCH}" > dist/${BINARI}-${GOOS}-${GOARCH}.txt
ls -l ./dist
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: ${{ env.BINARI }}-${{ env.RELEASE_CIBLE }}-${{ env.GOOS }}-${{ env.GOARCH }}
path: dist/${{ env.BINARI }}_${{ env.GOOS }}_${{ env.GOARCH }}

View file

@ -0,0 +1,49 @@
name: Pre Release Workflow
on:
push:
tags:
- '*rc*'
jobs:
set-release-target:
runs-on: docker
outputs:
release_cible: ${{ steps.setvar.outputs.release_cible }}
steps:
- name: Déterminer la release cible
id: setvar
run: |
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
TAG="${GITHUB_REF#refs/tags/}"
echo "release_cible=$TAG" >> $GITHUB_OUTPUT
elif [[ "${GITHUB_REF}" == "refs/heads/main" ]]; then
echo "release_cible=latest" >> $GITHUB_OUTPUT
else
echo "release_cible=unknown" >> $GITHUB_OUTPUT
fi
- name: Afficher la variable
run: echo "Release cible = ${{ steps.setvar.outputs.release_cible }}"
build:
runs-on: docker
needs: [set-release-target]
strategy:
matrix:
goos: [linux]
goarch: [amd64]
binaries: [db, metadata]
uses: ./.forgejo/workflows/build.yml
with:
tag: ${{ needs.set-release-target.outputs.release_cible }}
goos: ${{ matrix.goos }}
goarch: ${{ matrix.goarch }}
binari: ${{ matrix.binaries }}
secrets: inherit
prerelease:
runs-on: docker
needs: [set-release-target, build]
uses: ./.forgejo/workflows/release.yml
with:
tag: ${{ needs.set-release-target.outputs.release_cible }}
secrets: inherit

View file

@ -0,0 +1,56 @@
on:
workflow_call:
inputs:
tag:
required: true
type: string
jobs:
release:
runs-on: docker
env:
TOKEN: ${{ secrets.RELEASE }}
TAG: ${{ inputs.tag }}
steps:
- name: Download all build artifacts
uses: actions/download-artifact@v3
with:
path: dist/
- name: Publier tous les binaires
run: ls -lR dist/
- name: Install jq
run: |
apt-get update
apt-get install -y jq
- name: Create prerelease
run: |
curl -X POST \
-H "Authorization: token $TOKEN" \
-H "Content-Type: application/json" \
"https://git.g3e.fr/api/v1/repos/${{ github.repository }}/releases" \
-d @- <<EOF
{
"tag_name": "$TAG",
"name": "$TAG",
"body": "Prerelease automatique générée par la CI",
"draft": false,
"prerelease": true
}
EOF
- name: Upload asset
run: |
RELEASE_ID=$(curl -s \
-H "Authorization: token ${TOKEN}" \
https://git.g3e.fr/api/v1/repos/${{ github.repository }}/releases/tags/${TAG} \
| jq -r .id)
echo ${RELEASE_ID}
ls dist | while read tmp
do
FILE=$(ls "./dist/${tmp}")
echo ${FILE}
curl -X POST \
-H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/octet-stream" \
--data-binary @dist/${tmp}/${FILE} \
"https://git.g3e.fr/api/v1/repos/${{ github.repository }}/releases/${RELEASE_ID}/assets?name=${FILE}"
done

2
.gitignore vendored
View file

@ -25,3 +25,5 @@ go.work.sum
# env file
.env
# ignore local info
data/

0
cmd/agent/.keep Normal file
View file

194
cmd/db/main.go Normal file
View file

@ -0,0 +1,194 @@
package main
import (
"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 DB *badger.DB
func CheckInDB(dbName, id string) int {
prefix := []byte(dbName + "/")
key := []byte(dbName + "/" + id)
// vérifier si DB contient au moins une entrée avec ce préfixe
hasPrefix := false
DB.View(func(txn *badger.Txn) error {
it := txn.NewIterator(badger.DefaultIteratorOptions)
defer it.Close()
it.Seek(prefix)
if it.ValidForPrefix(prefix) {
hasPrefix = true
}
return nil
})
if !hasPrefix {
return 1
}
// vérifier si la clé existe
err := DB.View(func(txn *badger.Txn) error {
_, err := txn.Get(key)
return err
})
if err == badger.ErrKeyNotFound {
return 2
}
return 0
}
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 DeleteInDB(dbName, id string) error {
key := []byte(dbName + "/" + id + "/bash")
return DB.Update(func(txn *badger.Txn) error {
return txn.Delete(key)
})
}
func CountInDB(dbName, id string) int {
prefix := []byte(dbName + "/" + id + "/bash")
count := 0
DB.View(func(txn *badger.Txn) error {
it := txn.NewIterator(badger.DefaultIteratorOptions)
defer it.Close()
for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() {
count++
}
return nil
})
return count
}
func GetFromDB(dbName, id string) (string, error) {
key := []byte(dbName + "/" + id + "/bash")
var result string
err := DB.View(func(txn *badger.Txn) error {
item, err := txn.Get(key)
if err != nil {
return err
}
return item.Value(func(val []byte) error {
result = string(val)
return nil
})
})
return result, err
}
func printDB() {
err := DB.View(func(txn *badger.Txn) error {
it := txn.NewIterator(badger.DefaultIteratorOptions)
defer it.Close()
for it.Rewind(); it.Valid(); it.Next() {
item := it.Item()
key := item.Key()
err := item.Value(func(val []byte) error {
fmt.Printf("%s:%s\n", string(key), string(val))
return nil
})
if err != nil {
return err
}
}
return nil
})
if err != nil {
fmt.Println("Error reading DB:", err)
}
}
func main() {
conf, err := configuration.LoadConfig("/etc/two/agent.yml")
if err != nil {
fmt.Println(err)
return
}
DB = kv.InitDB(kv.Config{
Path: conf.Database.Path,
})
defer DB.Close()
if len(os.Args) < 2 {
fmt.Println("Usage: db <cmd> [args...]")
return
}
cmd := os.Args[1]
switch cmd {
case "check_in_db":
if len(os.Args) != 4 {
fmt.Println("Usage: check_in_db <db_name> <id>")
os.Exit(1)
}
ret := CheckInDB(os.Args[2], os.Args[3])
os.Exit(ret)
case "add_in_db":
if len(os.Args) < 4 {
fmt.Println("Usage: add_in_db <db_name> <line...>")
os.Exit(1)
}
line := strings.Join(os.Args[3:], ";")
if err := AddInDB(os.Args[2], line); err != nil {
fmt.Println("Error:", err)
os.Exit(1)
}
case "delete_in_db":
if len(os.Args) != 4 {
fmt.Println("Usage: delete_in_db <db_name> <id>")
os.Exit(1)
}
if err := DeleteInDB(os.Args[2], os.Args[3]); err != nil {
fmt.Println("Error:", err)
os.Exit(1)
}
case "count_in_db":
if len(os.Args) != 4 {
fmt.Println("Usage: count_in_db <db_name> <id>")
os.Exit(1)
}
count := CountInDB(os.Args[2], os.Args[3])
fmt.Println(count)
case "get_from_db":
if len(os.Args) != 4 {
fmt.Println("Usage: get_from_db <db_name> <id>")
os.Exit(1)
}
line, _ := GetFromDB(os.Args[2], os.Args[3])
fmt.Println(line)
case "print":
printDB()
os.Exit(1)
default:
fmt.Println("Unknown command:", cmd)
os.Exit(1)
}
}

9
cmd/metadata/main.go Normal file
View file

@ -0,0 +1,9 @@
package main
import (
"git.g3e.fr/syonad/two/internal/metadata"
)
func main() {
metadata.StartServer()
}

View file

@ -0,0 +1,2 @@
database:
path: "/var/lib/two/data/"

33
go.mod Normal file
View file

@ -0,0 +1,33 @@
module git.g3e.fr/syonad/two
go 1.23.8
require (
github.com/cespare/xxhash/v2 v2.3.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
github.com/fsnotify/fsnotify v1.9.0 // indirect
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/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
github.com/sagikazarmark/locafero v0.11.0 // indirect
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/spf13/viper v1.21.0 // indirect
github.com/subosito/gotenv v1.6.0 // 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
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/text v0.28.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
)

56
go.sum Normal file
View file

@ -0,0 +1,56 @@
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/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=
github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
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/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=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
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=
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=
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
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.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
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=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

0
internal/.keep Normal file
View file

View file

@ -0,0 +1,30 @@
package configuration
import (
"github.com/spf13/viper"
)
type Config struct {
Database struct {
Path string `mapstructure:"path"`
} `mapstructure:"database"`
}
func LoadConfig(path string) (*Config, error) {
v := viper.New()
v.SetConfigFile(path)
v.SetConfigType("yaml")
v.SetDefault("database.path", "/var/lib/two/data/")
if err := v.ReadInConfig(); err != nil {
return nil, err
}
var cfg Config
if err := v.Unmarshal(&cfg); err != nil {
return nil, err
}
return &cfg, nil
}

View file

@ -0,0 +1,75 @@
package metadata
import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
"time"
)
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")
)
func getIP(r *http.Request) string {
ip, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
return r.RemoteAddr
}
return ip
}
func rootHandler(w http.ResponseWriter, r *http.Request) {
ip := getIP(r)
path := r.URL.Path
timestamp := time.Now().Format(time.RFC3339)
userAgent := r.Header.Get("User-Agent")
log.Printf("[%s] Requête IP %s vers %s | User-Agent: %s", timestamp, ip, path, userAgent)
w.Header().Set("Content-Type", "text/yaml")
switch path {
case "/user-data":
fmt.Fprint(w, data.UserData)
case "/meta-data":
fmt.Fprint(w, data.MetaData)
case "/network-config":
fmt.Fprint(w, data.NetworkConfig)
case "/vendor-data":
fmt.Fprint(w, data.VendorData)
default:
http.NotFound(w, r)
}
}
func StartServer() {
flag.Parse()
if *file == "" {
log.Fatal("Vous devez spécifier un fichier via --file")
}
raw, err := ioutil.ReadFile(*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", *iface, *port)
log.Printf("Serveur NoCloud démarré sur http://%s/", address)
log.Fatal(http.ListenAndServe(address, nil))
}

View file

@ -0,0 +1,8 @@
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"`
}

0
pkg/.keep Normal file
View file

19
pkg/db/kv/init.go Normal file
View file

@ -0,0 +1,19 @@
package kv
import (
"github.com/dgraph-io/badger/v4"
)
func InitDB(conf Config) *badger.DB {
opts := badger.DefaultOptions(conf.Path)
opts.Logger = nil
opts.ValueLogFileSize = 10 << 20 // 10 Mo par fichier vlog
opts.NumMemtables = 1
opts.NumLevelZeroTables = 1
opts.NumLevelZeroTablesStall = 2
db, err := badger.Open(opts)
if err != nil {
panic(err)
}
return db
}

5
pkg/db/kv/struct.go Normal file
View file

@ -0,0 +1,5 @@
package kv
type Config struct {
Path string
}