From 9d97e11fc621d3a9219ef2882ab152a60a25466d Mon Sep 17 00:00:00 2001 From: juls0730 <62722391+juls0730@users.noreply.github.com> Date: Tue, 3 Dec 2024 03:21:12 -0600 Subject: [PATCH] scaffold CLI and fix logging --- .gitignore | 3 +- cmd/cli/config.json | 3 + cmd/cli/main.go | 205 +++++++++++++++++++++++++++++++++++++++++++- cmd/daemon/main.go | 2 +- go.mod | 24 ++++-- go.sum | 63 +++++++++++++- server/deploy.go | 29 ++++--- server/docker.go | 7 +- server/server.go | 36 ++++---- 9 files changed, 329 insertions(+), 43 deletions(-) create mode 100644 cmd/cli/config.json diff --git a/.gitignore b/.gitignore index f94c8f0..23435eb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ fluxd -flux \ No newline at end of file +flux +fluxdd/ \ No newline at end of file diff --git a/cmd/cli/config.json b/cmd/cli/config.json new file mode 100644 index 0000000..2b21a95 --- /dev/null +++ b/cmd/cli/config.json @@ -0,0 +1,3 @@ +{ + "deamon_url": "http://127.0.0.1:5647" +} \ No newline at end of file diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 7905807..d69c81f 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -1,5 +1,208 @@ 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 ") + 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) + } } diff --git a/cmd/daemon/main.go b/cmd/daemon/main.go index e087c5f..6422204 100644 --- a/cmd/daemon/main.go +++ b/cmd/daemon/main.go @@ -13,6 +13,6 @@ func main() { http.HandleFunc("POST /deploy", fluxServer.DeployHandler) 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)) } diff --git a/go.mod b/go.mod index 9c4dcce..ec9df84 100644 --- a/go.mod +++ b/go.mod @@ -2,26 +2,40 @@ module github.com/juls0730/fluxd 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 ( 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/docker/go-connections 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/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/joho/godotenv v1.5.1 // indirect - github.com/mattn/go-sqlite3 v1.14.24 // indirect + github.com/mattn/go-colorable v0.1.2 // indirect + github.com/mattn/go-isatty v0.0.8 // 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/image-spec v1.1.0 // indirect github.com/pkg/errors v0.9.1 // 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/exporters/otlp/otlptrace/otlptracehttp 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 - 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 ) diff --git a/go.sum b/go.sum index 131e90b..8b9cfaa 100644 --- a/go.sum +++ b/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/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/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= 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-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= 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/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 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/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 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/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 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/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/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/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/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= 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.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 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/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/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.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/go.mod h1:wZcGmeVO9nzP67aYSLDqXNWK87EZWhi7JWj1v7ZXf94= 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/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/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/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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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-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.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-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/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-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-20190507160741-ecd444e8653b/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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +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.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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 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-20191204190536-9bdfabe68543/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= diff --git a/server/deploy.go b/server/deploy.go index 3bcdf4d..efdfcaf 100644 --- a/server/deploy.go +++ b/server/deploy.go @@ -31,6 +31,7 @@ type ProjectConfig struct { func (s *FluxServer) DeployHandler(w http.ResponseWriter, r *http.Request) { err := r.ParseMultipartForm(10 << 30) // 10 GiB if err != nil { + log.Printf("Failed to parse multipart form: %v\n", err) http.Error(w, err.Error(), http.StatusBadRequest) return } @@ -53,7 +54,7 @@ func (s *FluxServer) DeployHandler(w http.ResponseWriter, r *http.Request) { var projectConfig ProjectConfig 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) return } @@ -73,58 +74,60 @@ func (s *FluxServer) DeployHandler(w http.ResponseWriter, r *http.Request) { 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) 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) return } + log.Printf("Preparing project %s...\n", projectConfig.Name) prepareCmd := exec.Command("go", "generate") prepareCmd.Dir = projectPath err = prepareCmd.Run() 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) return } + log.Printf("Building image for project %s...\n", projectConfig.Name) imageName := fmt.Sprintf("%s-image", projectConfig.Name) buildCmd := exec.Command("pack", "build", imageName, "--builder", s.config.Builder) buildCmd.Dir = projectPath err = buildCmd.Run() 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) return } containerID, err := s.containerManager.DeployContainer(r.Context(), imageName, projectConfig.Name, projectPath, projectConfig) 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) return } deploymentResult, err := s.db.Exec("INSERT INTO deployments (urls) VALUES (?)", strings.Join(projectConfig.Urls, ",")) 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) return } deploymentID, err := deploymentResult.LastInsertId() 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) return } _, err = s.db.Exec("INSERT INTO containers (container_id, deployment_id, status) VALUES (?, ?, ?)", containerID, deploymentID, "pending") 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) 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) configBytes, err := json.Marshal(projectConfig) 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) return } @@ -142,7 +145,7 @@ func (s *FluxServer) DeployHandler(w http.ResponseWriter, r *http.Request) { // 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) 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) return } @@ -150,7 +153,7 @@ func (s *FluxServer) DeployHandler(w http.ResponseWriter, r *http.Request) { // update app in the database appResult, err = s.db.Exec("UPDATE apps SET project_config = ?, deployment_id = ? WHERE name = ?", configBytes, deploymentID, projectConfig.Name) 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) return } @@ -158,7 +161,7 @@ func (s *FluxServer) DeployHandler(w http.ResponseWriter, r *http.Request) { appId, err := appResult.LastInsertId() 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) return } diff --git a/server/docker.go b/server/docker.go index 7cfe86f..ee6e3d4 100644 --- a/server/docker.go +++ b/server/docker.go @@ -32,6 +32,8 @@ func NewContainerManager() *ContainerManager { } 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")) 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 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 { 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{ Image: imageName, 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) } - log.Printf("Deployed new container: %s", containerName) + log.Printf("Deployed new container: %s\n", containerName) return resp.ID, nil } diff --git a/server/server.go b/server/server.go index ead38a3..bb16efa 100644 --- a/server/server.go +++ b/server/server.go @@ -49,47 +49,47 @@ func NewServer() *FluxServer { configPath := filepath.Join(rootDir, "config.json") if _, err := os.Stat(configPath); 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) 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 { - log.Fatalf("Failed to write config file: %v", err) + log.Fatalf("Failed to write config file: %v\n", err) } } configFile, err := os.ReadFile(configPath) 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 { - 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 { - 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")) if err != nil { - log.Fatalf("Failed to open database: %v", err) + log.Fatalf("Failed to open database: %v\n", err) } // create database schema schemaBytes, err := schema.ReadFile("schema.sql") 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)) if err != nil { - log.Fatalf("Failed to create database schema: %v", err) + log.Fatalf("Failed to create database schema: %v\n", err) } return &FluxServer{ @@ -102,27 +102,27 @@ func NewServer() *FluxServer { func (s *FluxServer) UploadAppCode(code io.Reader, projectConfig ProjectConfig) (string, error) { projectPath := filepath.Join(rootDir, "apps", projectConfig.Name) 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 } gzReader, err := gzip.NewReader(code) if err != nil { - log.Printf("Failed to create gzip reader: %v", err) + log.Printf("Failed to create gzip reader: %v\n", err) return "", err } defer gzReader.Close() tarReader := tar.NewReader(gzReader) - // Extract files + log.Printf("Extracting files for %s...\n", projectPath) for { header, err := tarReader.Next() if err == io.EOF { break } if err != nil { - log.Printf("Failed to read tar header: %v", err) + log.Printf("Failed to read tar header: %v\n", err) return "", err } @@ -133,24 +133,24 @@ func (s *FluxServer) UploadAppCode(code io.Reader, projectConfig ProjectConfig) switch header.Typeflag { case tar.TypeDir: 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 } case tar.TypeReg: 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 } outFile, err := os.Create(path) if err != nil { - log.Printf("Failed to extract file: %v", err) + log.Printf("Failed to extract file: %v\n", err) return "", err } defer outFile.Close() 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 } }