scaffold CLI and fix logging
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
fluxd
|
fluxd
|
||||||
flux
|
flux
|
||||||
|
fluxdd/
|
||||||
3
cmd/cli/config.json
Normal file
3
cmd/cli/config.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"deamon_url": "http://127.0.0.1:5647"
|
||||||
|
}
|
||||||
205
cmd/cli/main.go
205
cmd/cli/main.go
@@ -1,5 +1,208 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
func main() {
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
_ "embed"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"mime/multipart"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/briandowns/spinner"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed config.json
|
||||||
|
var config []byte
|
||||||
|
|
||||||
|
var configPath = filepath.Join(os.Getenv("HOME"), "/.config/flux")
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
DeamonURL string `json:"deamon_url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func compressDirectory() ([]byte, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
gzWriter := gzip.NewWriter(&buf)
|
||||||
|
tarWriter := tar.NewWriter(gzWriter)
|
||||||
|
|
||||||
|
err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if path == "flux.json" || info.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
header, err := tar.FileInfoHeader(info, "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
header.Name = path
|
||||||
|
|
||||||
|
if err := tarWriter.WriteHeader(header); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !info.IsDir() {
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
if _, err := io.Copy(tarWriter, file); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tarWriter.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := gzWriter.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if len(os.Args) < 2 {
|
||||||
|
fmt.Println("Usage: flux <command>")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
command := os.Args[1]
|
||||||
|
|
||||||
|
if _, err := os.Stat(filepath.Join(configPath, "config.json")); err != nil {
|
||||||
|
if err := os.MkdirAll(configPath, 0755); err != nil {
|
||||||
|
fmt.Printf("Failed to create config directory: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = os.WriteFile(filepath.Join(configPath, "config.json"), config, 0644); err != nil {
|
||||||
|
fmt.Printf("Failed to write config file: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var config Config
|
||||||
|
configBytes, err := os.ReadFile(filepath.Join(configPath, "config.json"))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to read config file: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(configBytes, &config); err != nil {
|
||||||
|
fmt.Printf("Failed to parse config file: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch command {
|
||||||
|
case "deploy":
|
||||||
|
loadingSpinner := spinner.New(spinner.CharSets[14], 100*time.Millisecond)
|
||||||
|
loadingSpinner.Suffix = " Deploying"
|
||||||
|
loadingSpinner.Start()
|
||||||
|
|
||||||
|
buf, err := compressDirectory()
|
||||||
|
if err != nil {
|
||||||
|
loadingSpinner.Stop()
|
||||||
|
|
||||||
|
fmt.Printf("Failed to compress directory: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
body := &bytes.Buffer{}
|
||||||
|
writer := multipart.NewWriter(body)
|
||||||
|
configPart, err := writer.CreateFormFile("config", "flux.json")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
loadingSpinner.Stop()
|
||||||
|
|
||||||
|
fmt.Printf("Failed to create config part: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fluxConfigFile, err := os.Open("flux.json")
|
||||||
|
if err != nil {
|
||||||
|
loadingSpinner.Stop()
|
||||||
|
|
||||||
|
fmt.Printf("Failed to open flux.json: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
defer fluxConfigFile.Close()
|
||||||
|
|
||||||
|
if _, err := io.Copy(configPart, fluxConfigFile); err != nil {
|
||||||
|
loadingSpinner.Stop()
|
||||||
|
|
||||||
|
fmt.Printf("Failed to write config part: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
codePart, err := writer.CreateFormFile("code", "code.tar.gz")
|
||||||
|
if err != nil {
|
||||||
|
loadingSpinner.Stop()
|
||||||
|
|
||||||
|
fmt.Printf("Failed to create code part: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := codePart.Write(buf); err != nil {
|
||||||
|
loadingSpinner.Stop()
|
||||||
|
|
||||||
|
fmt.Printf("Failed to write code part: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writer.Close(); err != nil {
|
||||||
|
loadingSpinner.Stop()
|
||||||
|
|
||||||
|
fmt.Printf("Failed to close writer: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.Post(config.DeamonURL+"/deploy", "multipart/form-data; boundary="+writer.Boundary(), body)
|
||||||
|
if err != nil {
|
||||||
|
loadingSpinner.Stop()
|
||||||
|
|
||||||
|
fmt.Printf("Failed to send request: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
loadingSpinner.Stop()
|
||||||
|
|
||||||
|
responseBody, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("error reading response body: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(responseBody) > 0 && responseBody[len(responseBody)-1] == '\n' {
|
||||||
|
responseBody = responseBody[:len(responseBody)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Deploy failed: %s\n", responseBody)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
loadingSpinner.Stop()
|
||||||
|
fmt.Println("Deployed successfully!")
|
||||||
|
default:
|
||||||
|
fmt.Println("Unknown command:", command)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,6 @@ func main() {
|
|||||||
http.HandleFunc("POST /deploy", fluxServer.DeployHandler)
|
http.HandleFunc("POST /deploy", fluxServer.DeployHandler)
|
||||||
http.HandleFunc("GET /apps", fluxServer.ListAppsHandler)
|
http.HandleFunc("GET /apps", fluxServer.ListAppsHandler)
|
||||||
|
|
||||||
log.Printf("Fluxd started on http://127.0.0.1:5647")
|
log.Printf("Fluxd started on http://127.0.0.1:5647\n")
|
||||||
log.Fatal(http.ListenAndServe(":5647", nil))
|
log.Fatal(http.ListenAndServe(":5647", nil))
|
||||||
}
|
}
|
||||||
|
|||||||
24
go.mod
24
go.mod
@@ -2,26 +2,40 @@ module github.com/juls0730/fluxd
|
|||||||
|
|
||||||
go 1.23.3
|
go 1.23.3
|
||||||
|
|
||||||
require github.com/docker/docker v27.3.1+incompatible
|
require (
|
||||||
|
github.com/briandowns/spinner v1.23.1
|
||||||
|
github.com/docker/docker v27.3.1+incompatible
|
||||||
|
github.com/docker/go-connections v0.5.0
|
||||||
|
github.com/joho/godotenv v1.5.1
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.24
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Microsoft/go-winio v0.4.14 // indirect
|
github.com/Microsoft/go-winio v0.4.14 // indirect
|
||||||
|
github.com/containerd/log v0.1.0 // indirect
|
||||||
github.com/distribution/reference v0.6.0 // indirect
|
github.com/distribution/reference v0.6.0 // indirect
|
||||||
github.com/docker/go-connections v0.5.0 // indirect
|
|
||||||
github.com/docker/go-units v0.5.0 // indirect
|
github.com/docker/go-units v0.5.0 // indirect
|
||||||
|
github.com/fatih/color v1.7.0 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/go-logr/logr v1.4.2 // indirect
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/joho/godotenv v1.5.1 // indirect
|
github.com/mattn/go-colorable v0.1.2 // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.14.24 // indirect
|
github.com/mattn/go-isatty v0.0.8 // indirect
|
||||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||||
|
github.com/moby/term v0.5.0 // indirect
|
||||||
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 // indirect
|
||||||
go.opentelemetry.io/otel v1.32.0 // indirect
|
go.opentelemetry.io/otel v1.32.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.32.0 // indirect
|
go.opentelemetry.io/otel/metric v1.32.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/sdk v1.32.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.32.0 // indirect
|
go.opentelemetry.io/otel/trace v1.32.0 // indirect
|
||||||
golang.org/x/sys v0.1.0 // indirect
|
golang.org/x/sys v0.27.0 // indirect
|
||||||
|
golang.org/x/term v0.1.0 // indirect
|
||||||
|
golang.org/x/time v0.8.0 // indirect
|
||||||
|
gotest.tools/v3 v3.5.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
63
go.sum
63
go.sum
@@ -1,5 +1,14 @@
|
|||||||
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||||
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
||||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||||
|
github.com/briandowns/spinner v1.23.1 h1:t5fDPmScwUjozhDj4FA46p5acZWIPXYE30qW2Ptu650=
|
||||||
|
github.com/briandowns/spinner v1.23.1/go.mod h1:LaZeM4wm2Ywy6vO571mvhQNRcWfRUnXOs0RcKV0wYKM=
|
||||||
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
|
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
|
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||||
|
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||||
@@ -9,6 +18,8 @@ github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj
|
|||||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
|
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||||
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
@@ -18,15 +29,29 @@ 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-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 h1:ad0vkEBuk23VJzZR9nkLVG0YAoN9coASF1GusYX6AlU=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DWPB5BEWfZISOzSP5K2sbLea0=
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
||||||
|
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
|
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
|
||||||
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
|
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
|
||||||
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||||
|
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||||
|
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||||
|
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||||
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||||
@@ -34,20 +59,33 @@ github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2sz
|
|||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||||
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 h1:DheMAlT6POBP+gh8RUH19EOTnQIor5QE0uSRPtzCpSw=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 h1:DheMAlT6POBP+gh8RUH19EOTnQIor5QE0uSRPtzCpSw=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0/go.mod h1:wZcGmeVO9nzP67aYSLDqXNWK87EZWhi7JWj1v7ZXf94=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0/go.mod h1:wZcGmeVO9nzP67aYSLDqXNWK87EZWhi7JWj1v7ZXf94=
|
||||||
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
|
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
|
||||||
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
|
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 h1:IJFEoHiytixx8cMiVAO+GmHR6Frwu+u5Ur8njpFO6Ac=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0/go.mod h1:3rHrKNtLIoS0oZwkY2vxi+oJcwFRWdtUyRII+so45p8=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 h1:cMyu9O88joYEaI47CnQkxO1XZdpoTF9fEnW2duIddhw=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0/go.mod h1:6Am3rn7P9TVVeXYG+wtcGE7IE1tsQ+bP3AuWcKt/gOI=
|
||||||
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
|
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
|
||||||
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
|
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
|
||||||
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
|
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
|
||||||
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
|
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
|
||||||
|
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
||||||
|
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
@@ -57,18 +95,27 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||||
|
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
|
||||||
|
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||||
|
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||||
|
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||||
|
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
@@ -77,3 +124,15 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
|||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 h1:M0KvPgPmDZHPlbRbaNU1APr28TvwvvdUPlSv7PUvy8g=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:dguCy7UOdZhTvLzDyt15+rOrawrpM4q7DD9dQ1P11P4=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 h1:XVhgTWWV3kGQlwJHR3upFWZeTsei6Oks1apkZSeonIE=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||||
|
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
|
||||||
|
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
|
||||||
|
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||||
|
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||||
|
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ type ProjectConfig struct {
|
|||||||
func (s *FluxServer) DeployHandler(w http.ResponseWriter, r *http.Request) {
|
func (s *FluxServer) DeployHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
err := r.ParseMultipartForm(10 << 30) // 10 GiB
|
err := r.ParseMultipartForm(10 << 30) // 10 GiB
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Failed to parse multipart form: %v\n", err)
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -53,7 +54,7 @@ func (s *FluxServer) DeployHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
var projectConfig ProjectConfig
|
var projectConfig ProjectConfig
|
||||||
if err := json.NewDecoder(deployRequest.Config).Decode(&projectConfig); err != nil {
|
if err := json.NewDecoder(deployRequest.Config).Decode(&projectConfig); err != nil {
|
||||||
log.Printf("Failed to decode config: %v", err)
|
log.Printf("Failed to decode config: %v\n", err)
|
||||||
http.Error(w, "Invalid flux.json", http.StatusBadRequest)
|
http.Error(w, "Invalid flux.json", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -73,58 +74,60 @@ func (s *FluxServer) DeployHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Deploying project %s to %s", projectConfig.Name, projectConfig.Urls)
|
log.Printf("Deploying project %s to %s\n", projectConfig.Name, projectConfig.Urls)
|
||||||
|
|
||||||
projectPath, err := s.UploadAppCode(deployRequest.Code, projectConfig)
|
projectPath, err := s.UploadAppCode(deployRequest.Code, projectConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to upload code: %v", err)
|
log.Printf("Failed to upload code: %v\n", err)
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("Preparing project %s...\n", projectConfig.Name)
|
||||||
prepareCmd := exec.Command("go", "generate")
|
prepareCmd := exec.Command("go", "generate")
|
||||||
prepareCmd.Dir = projectPath
|
prepareCmd.Dir = projectPath
|
||||||
err = prepareCmd.Run()
|
err = prepareCmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to prepare project: %s", err)
|
log.Printf("Failed to prepare project: %s\n", err)
|
||||||
http.Error(w, fmt.Sprintf("Failed to prepare project: %s", err), http.StatusInternalServerError)
|
http.Error(w, fmt.Sprintf("Failed to prepare project: %s", err), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("Building image for project %s...\n", projectConfig.Name)
|
||||||
imageName := fmt.Sprintf("%s-image", projectConfig.Name)
|
imageName := fmt.Sprintf("%s-image", projectConfig.Name)
|
||||||
buildCmd := exec.Command("pack", "build", imageName, "--builder", s.config.Builder)
|
buildCmd := exec.Command("pack", "build", imageName, "--builder", s.config.Builder)
|
||||||
buildCmd.Dir = projectPath
|
buildCmd.Dir = projectPath
|
||||||
err = buildCmd.Run()
|
err = buildCmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to build image: %s", err)
|
log.Printf("Failed to build image: %s\n", err)
|
||||||
http.Error(w, fmt.Sprintf("Failed to build image: %s", err), http.StatusInternalServerError)
|
http.Error(w, fmt.Sprintf("Failed to build image: %s", err), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
containerID, err := s.containerManager.DeployContainer(r.Context(), imageName, projectConfig.Name, projectPath, projectConfig)
|
containerID, err := s.containerManager.DeployContainer(r.Context(), imageName, projectConfig.Name, projectPath, projectConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to deploy container: %v", err)
|
log.Printf("Failed to deploy container: %v\n", err)
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
deploymentResult, err := s.db.Exec("INSERT INTO deployments (urls) VALUES (?)", strings.Join(projectConfig.Urls, ","))
|
deploymentResult, err := s.db.Exec("INSERT INTO deployments (urls) VALUES (?)", strings.Join(projectConfig.Urls, ","))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to insert deployment: %v", err)
|
log.Printf("Failed to insert deployment: %v\n", err)
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
deploymentID, err := deploymentResult.LastInsertId()
|
deploymentID, err := deploymentResult.LastInsertId()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to get deployment id: %v", err)
|
log.Printf("Failed to get deployment id: %v\n", err)
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = s.db.Exec("INSERT INTO containers (container_id, deployment_id, status) VALUES (?, ?, ?)", containerID, deploymentID, "pending")
|
_, err = s.db.Exec("INSERT INTO containers (container_id, deployment_id, status) VALUES (?, ?, ?)", containerID, deploymentID, "pending")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to get container id: %v", err)
|
log.Printf("Failed to get container id: %v\n", err)
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -132,7 +135,7 @@ func (s *FluxServer) DeployHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
appExists := s.db.QueryRow("SELECT * FROM apps WHERE name = ?", projectConfig.Name)
|
appExists := s.db.QueryRow("SELECT * FROM apps WHERE name = ?", projectConfig.Name)
|
||||||
configBytes, err := json.Marshal(projectConfig)
|
configBytes, err := json.Marshal(projectConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to marshal project config: %v", err)
|
log.Printf("Failed to marshal project config: %v\n", err)
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -142,7 +145,7 @@ func (s *FluxServer) DeployHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
// create app in the database
|
// create app in the database
|
||||||
appResult, err = s.db.Exec("INSERT INTO apps (name, image, project_path, project_config, deployment_id) VALUES (?, ?, ?, ?, ?)", projectConfig.Name, imageName, projectPath, configBytes, deploymentID)
|
appResult, err = s.db.Exec("INSERT INTO apps (name, image, project_path, project_config, deployment_id) VALUES (?, ?, ?, ?, ?)", projectConfig.Name, imageName, projectPath, configBytes, deploymentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to insert app: %v", err)
|
log.Printf("Failed to insert app: %v\n", err)
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -150,7 +153,7 @@ func (s *FluxServer) DeployHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
// update app in the database
|
// update app in the database
|
||||||
appResult, err = s.db.Exec("UPDATE apps SET project_config = ?, deployment_id = ? WHERE name = ?", configBytes, deploymentID, projectConfig.Name)
|
appResult, err = s.db.Exec("UPDATE apps SET project_config = ?, deployment_id = ? WHERE name = ?", configBytes, deploymentID, projectConfig.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to update app: %v", err)
|
log.Printf("Failed to update app: %v\n", err)
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -158,7 +161,7 @@ func (s *FluxServer) DeployHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
appId, err := appResult.LastInsertId()
|
appId, err := appResult.LastInsertId()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to get app id: %v", err)
|
log.Printf("Failed to get app id: %v\n", err)
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ func NewContainerManager() *ContainerManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cm *ContainerManager) DeployContainer(ctx context.Context, imageName, containerPrefix, projectPath string, projectConfig ProjectConfig) (string, error) {
|
func (cm *ContainerManager) DeployContainer(ctx context.Context, imageName, containerPrefix, projectPath string, projectConfig ProjectConfig) (string, error) {
|
||||||
|
log.Printf("Deploying container with image %s\n", imageName)
|
||||||
|
|
||||||
containerName := fmt.Sprintf("%s-%s", containerPrefix, time.Now().Format("20060102-150405"))
|
containerName := fmt.Sprintf("%s-%s", containerPrefix, time.Now().Format("20060102-150405"))
|
||||||
|
|
||||||
existingContainers, err := cm.findExistingContainers(ctx, containerPrefix)
|
existingContainers, err := cm.findExistingContainers(ctx, containerPrefix)
|
||||||
@@ -41,7 +43,7 @@ func (cm *ContainerManager) DeployContainer(ctx context.Context, imageName, cont
|
|||||||
|
|
||||||
// TODO: swap containers if they are running and have the same image so that we can have a constant uptime
|
// TODO: swap containers if they are running and have the same image so that we can have a constant uptime
|
||||||
for _, existingContainer := range existingContainers {
|
for _, existingContainer := range existingContainers {
|
||||||
log.Printf("Stopping existing container: %s", existingContainer)
|
log.Printf("Stopping existing container: %s\n", existingContainer)
|
||||||
|
|
||||||
if err := cm.dockerClient.ContainerStop(ctx, existingContainer, container.StopOptions{}); err != nil {
|
if err := cm.dockerClient.ContainerStop(ctx, existingContainer, container.StopOptions{}); err != nil {
|
||||||
return "", fmt.Errorf("Failed to stop existing container: %v", err)
|
return "", fmt.Errorf("Failed to stop existing container: %v", err)
|
||||||
@@ -69,6 +71,7 @@ func (cm *ContainerManager) DeployContainer(ctx context.Context, imageName, cont
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("Creating and starting container %s...\n", containerName)
|
||||||
resp, err := cm.dockerClient.ContainerCreate(ctx, &container.Config{
|
resp, err := cm.dockerClient.ContainerCreate(ctx, &container.Config{
|
||||||
Image: imageName,
|
Image: imageName,
|
||||||
Env: projectConfig.Environment,
|
Env: projectConfig.Environment,
|
||||||
@@ -98,7 +101,7 @@ func (cm *ContainerManager) DeployContainer(ctx context.Context, imageName, cont
|
|||||||
return "", fmt.Errorf("Failed to start container: %v", err)
|
return "", fmt.Errorf("Failed to start container: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Deployed new container: %s", containerName)
|
log.Printf("Deployed new container: %s\n", containerName)
|
||||||
return resp.ID, nil
|
return resp.ID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,47 +49,47 @@ func NewServer() *FluxServer {
|
|||||||
configPath := filepath.Join(rootDir, "config.json")
|
configPath := filepath.Join(rootDir, "config.json")
|
||||||
if _, err := os.Stat(configPath); err != nil {
|
if _, err := os.Stat(configPath); err != nil {
|
||||||
if err := os.MkdirAll(rootDir, 0755); err != nil {
|
if err := os.MkdirAll(rootDir, 0755); err != nil {
|
||||||
log.Fatalf("Failed to create fluxd directory: %v", err)
|
log.Fatalf("Failed to create fluxd directory: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
configBytes, err := json.Marshal(DefaultConfig)
|
configBytes, err := json.Marshal(DefaultConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to marshal default config: %v", err)
|
log.Fatalf("Failed to marshal default config: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Config file not found, creating default config file at %s", configPath)
|
log.Printf("Config file not found, creating default config file at %s\n", configPath)
|
||||||
if err := os.WriteFile(configPath, configBytes, 0644); err != nil {
|
if err := os.WriteFile(configPath, configBytes, 0644); err != nil {
|
||||||
log.Fatalf("Failed to write config file: %v", err)
|
log.Fatalf("Failed to write config file: %v\n", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
configFile, err := os.ReadFile(configPath)
|
configFile, err := os.ReadFile(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to read config file: %v", err)
|
log.Fatalf("Failed to read config file: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := json.Unmarshal(configFile, &serverConfig); err != nil {
|
if err := json.Unmarshal(configFile, &serverConfig); err != nil {
|
||||||
log.Fatalf("Failed to parse config file: %v", err)
|
log.Fatalf("Failed to parse config file: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.MkdirAll(filepath.Join(rootDir, "apps"), 0755); err != nil {
|
if err := os.MkdirAll(filepath.Join(rootDir, "apps"), 0755); err != nil {
|
||||||
log.Fatalf("Failed to create apps directory: %v", err)
|
log.Fatalf("Failed to create apps directory: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
db, err := sql.Open("sqlite3", filepath.Join(rootDir, "fluxd.db"))
|
db, err := sql.Open("sqlite3", filepath.Join(rootDir, "fluxd.db"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to open database: %v", err)
|
log.Fatalf("Failed to open database: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// create database schema
|
// create database schema
|
||||||
schemaBytes, err := schema.ReadFile("schema.sql")
|
schemaBytes, err := schema.ReadFile("schema.sql")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to read schema file: %v", err)
|
log.Fatalf("Failed to read schema file: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = db.Exec(string(schemaBytes))
|
_, err = db.Exec(string(schemaBytes))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to create database schema: %v", err)
|
log.Fatalf("Failed to create database schema: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &FluxServer{
|
return &FluxServer{
|
||||||
@@ -102,27 +102,27 @@ func NewServer() *FluxServer {
|
|||||||
func (s *FluxServer) UploadAppCode(code io.Reader, projectConfig ProjectConfig) (string, error) {
|
func (s *FluxServer) UploadAppCode(code io.Reader, projectConfig ProjectConfig) (string, error) {
|
||||||
projectPath := filepath.Join(rootDir, "apps", projectConfig.Name)
|
projectPath := filepath.Join(rootDir, "apps", projectConfig.Name)
|
||||||
if err := os.MkdirAll(projectPath, 0755); err != nil {
|
if err := os.MkdirAll(projectPath, 0755); err != nil {
|
||||||
log.Printf("Failed to create project directory: %v", err)
|
log.Printf("Failed to create project directory: %v\n", err)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
gzReader, err := gzip.NewReader(code)
|
gzReader, err := gzip.NewReader(code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to create gzip reader: %v", err)
|
log.Printf("Failed to create gzip reader: %v\n", err)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
defer gzReader.Close()
|
defer gzReader.Close()
|
||||||
|
|
||||||
tarReader := tar.NewReader(gzReader)
|
tarReader := tar.NewReader(gzReader)
|
||||||
|
|
||||||
// Extract files
|
log.Printf("Extracting files for %s...\n", projectPath)
|
||||||
for {
|
for {
|
||||||
header, err := tarReader.Next()
|
header, err := tarReader.Next()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to read tar header: %v", err)
|
log.Printf("Failed to read tar header: %v\n", err)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,24 +133,24 @@ func (s *FluxServer) UploadAppCode(code io.Reader, projectConfig ProjectConfig)
|
|||||||
switch header.Typeflag {
|
switch header.Typeflag {
|
||||||
case tar.TypeDir:
|
case tar.TypeDir:
|
||||||
if err := os.MkdirAll(path, 0755); err != nil {
|
if err := os.MkdirAll(path, 0755); err != nil {
|
||||||
log.Printf("Failed to extract directory: %v", err)
|
log.Printf("Failed to extract directory: %v\n", err)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
case tar.TypeReg:
|
case tar.TypeReg:
|
||||||
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
||||||
log.Printf("Failed to extract directory: %v", err)
|
log.Printf("Failed to extract directory: %v\n", err)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
outFile, err := os.Create(path)
|
outFile, err := os.Create(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to extract file: %v", err)
|
log.Printf("Failed to extract file: %v\n", err)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
defer outFile.Close()
|
defer outFile.Close()
|
||||||
|
|
||||||
if _, err := io.Copy(outFile, tarReader); err != nil {
|
if _, err := io.Copy(outFile, tarReader); err != nil {
|
||||||
log.Printf("Failed to copy file during extraction: %v", err)
|
log.Printf("Failed to copy file during extraction: %v\n", err)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user