Initial commit
This commit is contained in:
commit
7c111f97ab
44
.air.toml
Normal file
44
.air.toml
Normal file
@ -0,0 +1,44 @@
|
||||
root = "."
|
||||
testdata_dir = "testdata"
|
||||
tmp_dir = "tmp"
|
||||
|
||||
[build]
|
||||
args_bin = []
|
||||
bin = "./tmp/main"
|
||||
cmd = "go build -o ./tmp/main ./cmd/virteen/main.go"
|
||||
delay = 0
|
||||
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
|
||||
exclude_file = []
|
||||
exclude_regex = ["_test.go"]
|
||||
exclude_unchanged = false
|
||||
follow_symlink = false
|
||||
full_bin = ""
|
||||
include_dir = []
|
||||
include_ext = ["go", "tpl", "tmpl", "html"]
|
||||
include_file = []
|
||||
kill_delay = "0s"
|
||||
log = "build-errors.log"
|
||||
poll = false
|
||||
poll_interval = 0
|
||||
rerun = false
|
||||
rerun_delay = 500
|
||||
send_interrupt = false
|
||||
stop_on_error = false
|
||||
|
||||
[color]
|
||||
app = ""
|
||||
build = "yellow"
|
||||
main = "magenta"
|
||||
runner = "green"
|
||||
watcher = "cyan"
|
||||
|
||||
[log]
|
||||
main_only = false
|
||||
time = false
|
||||
|
||||
[misc]
|
||||
clean_on_exit = false
|
||||
|
||||
[screen]
|
||||
clear_on_rebuild = false
|
||||
keep_scroll = true
|
1
.env.sample
Normal file
1
.env.sample
Normal file
@ -0,0 +1 @@
|
||||
JWT_SECRET=
|
27
.gitignore
vendored
Normal file
27
.gitignore
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
# If you prefer the allow list template instead of the deny list, see community template:
|
||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||
#
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
|
||||
# Air
|
||||
tmp
|
||||
|
||||
# secrets
|
||||
.env
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 niku
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
2
README.md
Normal file
2
README.md
Normal file
@ -0,0 +1,2 @@
|
||||
# virteen
|
||||
Toy virtualization manager, inspired by Proxmox Virtual Environment.
|
18
cmd/virteen/main.go
Normal file
18
cmd/virteen/main.go
Normal file
@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"git.cesium.pw/niku/virteen/internal/app"
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := godotenv.Load(".env"); err != nil {
|
||||
panic("Error loading .env file")
|
||||
}
|
||||
|
||||
if err := app.Run(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
149
go.mod
Normal file
149
go.mod
Normal file
@ -0,0 +1,149 @@
|
||||
module git.cesium.pw/niku/virteen
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/containers/common v0.52.0
|
||||
github.com/containers/podman/v4 v4.5.0
|
||||
github.com/gofiber/fiber/v2 v2.46.0
|
||||
github.com/msteinert/pam v1.1.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/BurntSushi/toml v1.2.1 // indirect
|
||||
github.com/MicahParks/keyfunc/v2 v2.1.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/Microsoft/hcsshim v0.10.0-rc.8 // indirect
|
||||
github.com/VividCortex/ewma v1.2.0 // indirect
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/chzyer/readline v1.5.1 // indirect
|
||||
github.com/cilium/ebpf v0.9.1 // indirect
|
||||
github.com/container-orchestrated-devices/container-device-interface v0.5.4 // indirect
|
||||
github.com/containerd/cgroups v1.1.0 // indirect
|
||||
github.com/containerd/containerd v1.7.1 // indirect
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
|
||||
github.com/containers/buildah v1.30.0 // indirect
|
||||
github.com/containers/image/v5 v5.25.0 // indirect
|
||||
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect
|
||||
github.com/containers/ocicrypt v1.1.7 // indirect
|
||||
github.com/containers/psgo v1.8.0 // indirect
|
||||
github.com/containers/storage v1.46.1 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
|
||||
github.com/disiqueira/gotree/v3 v3.0.2 // indirect
|
||||
github.com/docker/distribution v2.8.1+incompatible // indirect
|
||||
github.com/docker/docker v23.0.3+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.7.0 // indirect
|
||||
github.com/docker/go-connections v0.4.1-0.20210727194412-58542c764a11 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/go-openapi/analysis v0.21.4 // indirect
|
||||
github.com/go-openapi/errors v0.20.3 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.0 // indirect
|
||||
github.com/go-openapi/loads v0.21.2 // indirect
|
||||
github.com/go-openapi/runtime v0.25.0 // indirect
|
||||
github.com/go-openapi/spec v0.20.8 // indirect
|
||||
github.com/go-openapi/strfmt v0.21.7 // indirect
|
||||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/go-openapi/validate v0.22.1 // indirect
|
||||
github.com/godbus/dbus/v5 v5.1.1-0.20221029134443-4b691ce883d5 // indirect
|
||||
github.com/gofiber/contrib/jwt v1.0.1 // indirect
|
||||
github.com/gofiber/jwt/v2 v2.2.7 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.0.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/go-containerregistry v0.14.0 // indirect
|
||||
github.com/google/go-intervals v0.0.2 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/gorilla/schema v1.2.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/imdario/mergo v0.3.15 // indirect
|
||||
github.com/jinzhu/copier v0.3.5 // indirect
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.16.5 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6-0.20220930104621-17e8dac29df8 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/letsencrypt/boulder v0.0.0-20230213213521-fdfea0d469b6 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/manifoldco/promptui v0.9.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/mattn/go-shellwords v1.0.12 // indirect
|
||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||
github.com/mistifyio/go-zfs/v3 v3.0.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/moby/sys/mountinfo v0.6.2 // indirect
|
||||
github.com/moby/term v0.0.0-20221120202655-abb19827d345 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect
|
||||
github.com/opencontainers/runc v1.1.5 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.1.0-rc.1 // indirect
|
||||
github.com/opencontainers/runtime-tools v0.9.1-0.20230317050512-e931285f4b69 // indirect
|
||||
github.com/opencontainers/selinux v1.11.0 // indirect
|
||||
github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f // indirect
|
||||
github.com/philhofer/fwd v1.1.2 // indirect
|
||||
github.com/pkg/sftp v1.13.5 // indirect
|
||||
github.com/proglottis/gpgme v0.1.3 // indirect
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect
|
||||
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
|
||||
github.com/sigstore/fulcio v1.2.0 // indirect
|
||||
github.com/sigstore/rekor v1.1.0 // indirect
|
||||
github.com/sigstore/sigstore v1.6.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 // indirect
|
||||
github.com/sylabs/sif/v2 v2.11.1 // indirect
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
|
||||
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
|
||||
github.com/theupdateframework/go-tuf v0.5.2 // indirect
|
||||
github.com/tinylib/msgp v1.1.8 // indirect
|
||||
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
|
||||
github.com/ulikunitz/xz v0.5.11 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.47.0 // indirect
|
||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||
github.com/vbatts/tar-split v0.11.3 // indirect
|
||||
github.com/vbauerster/mpb/v8 v8.3.0 // indirect
|
||||
go.etcd.io/bbolt v1.3.7 // indirect
|
||||
go.mongodb.org/mongo-driver v1.11.3 // indirect
|
||||
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
golang.org/x/crypto v0.8.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
|
||||
golang.org/x/mod v0.9.0 // indirect
|
||||
golang.org/x/net v0.9.0 // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
golang.org/x/sys v0.8.0 // indirect
|
||||
golang.org/x/term v0.7.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
golang.org/x/tools v0.7.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect
|
||||
google.golang.org/grpc v1.54.0 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/go-jose/go-jose.v2 v2.6.1 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
)
|
22
internal/app/api/router.go
Normal file
22
internal/app/api/router.go
Normal file
@ -0,0 +1,22 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
v1 "git.cesium.pw/niku/virteen/internal/app/api/v1"
|
||||
"git.cesium.pw/niku/virteen/internal/middleware"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/helmet"
|
||||
)
|
||||
|
||||
func ConfigureRoutes(app *fiber.App) error {
|
||||
api := app.Group("/api")
|
||||
|
||||
api.Use(helmet.New())
|
||||
api.Use(middleware.ErrorHandler())
|
||||
|
||||
err := v1.ConfigureRoutes(api)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
9
internal/app/api/v1/ping.go
Normal file
9
internal/app/api/v1/ping.go
Normal file
@ -0,0 +1,9 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func Ping(c *fiber.Ctx) error {
|
||||
return c.SendString("pong")
|
||||
}
|
43
internal/app/api/v1/router.go
Normal file
43
internal/app/api/v1/router.go
Normal file
@ -0,0 +1,43 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"git.cesium.pw/niku/virteen/internal/auth"
|
||||
"git.cesium.pw/niku/virteen/internal/middleware"
|
||||
"git.cesium.pw/niku/virteen/internal/podman"
|
||||
"git.cesium.pw/niku/virteen/internal/podman/models"
|
||||
"git.cesium.pw/niku/virteen/internal/podman/services"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/monitor"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func ConfigureRoutes(router fiber.Router) error {
|
||||
v1 := router.Group("/v1")
|
||||
|
||||
ac := auth.NewPamAuthController()
|
||||
ag := v1.Group("/auth")
|
||||
ag.Post("/token", ac.GetToken)
|
||||
|
||||
v1.Get("/ping", Ping)
|
||||
v1.Get("/metrics", monitor.New())
|
||||
|
||||
v1.Use(middleware.Protected())
|
||||
|
||||
var cs models.PodmanService
|
||||
cs, err := services.NewPodmanContainerService()
|
||||
if err != nil {
|
||||
panic(errors.Wrapf(err, "failed to build podman container service"))
|
||||
}
|
||||
|
||||
cc := podman.NewPodmanController(&cs)
|
||||
|
||||
cg := v1.Group("/containers")
|
||||
cg.Get("/", cc.ListContainers)
|
||||
cg.Post("/", cc.CreateContainer)
|
||||
|
||||
ccg := cg.Group("/:name")
|
||||
ccg.Put("/status", cc.UpdateContainerStatus)
|
||||
ccg.Delete("/", cc.DeleteContainer)
|
||||
|
||||
return nil
|
||||
}
|
19
internal/app/main.go
Normal file
19
internal/app/main.go
Normal file
@ -0,0 +1,19 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"git.cesium.pw/niku/virteen/internal/app/api"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/compress"
|
||||
"github.com/gofiber/fiber/v2/middleware/logger"
|
||||
)
|
||||
|
||||
func Run() error {
|
||||
app := fiber.New()
|
||||
|
||||
app.Use(logger.New())
|
||||
app.Use(compress.New())
|
||||
|
||||
api.ConfigureRoutes(app)
|
||||
|
||||
return app.Listen(":3000")
|
||||
}
|
52
internal/auth/auth_controller.go
Normal file
52
internal/auth/auth_controller.go
Normal file
@ -0,0 +1,52 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"git.cesium.pw/niku/virteen/internal/auth/services"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/golang-jwt/jwt"
|
||||
)
|
||||
|
||||
type PamAuthController struct {
|
||||
pam *services.PamAuthService
|
||||
}
|
||||
|
||||
func NewPamAuthController() *PamAuthController {
|
||||
return &PamAuthController{
|
||||
pam: services.NewPamAuthService(),
|
||||
}
|
||||
}
|
||||
|
||||
type GetTokenBody struct {
|
||||
User string `json:"user"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
func (pac *PamAuthController) GetToken(ctx *fiber.Ctx) error {
|
||||
var body GetTokenBody
|
||||
if err := ctx.BodyParser(&body); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
isValid := (*pac.pam).IsValidUser(body.User, body.Password)
|
||||
if isValid == false {
|
||||
return fmt.Errorf("invalid user or password")
|
||||
}
|
||||
|
||||
claims := jwt.MapClaims{
|
||||
"name": body.User,
|
||||
"exp": time.Now().Add(time.Hour * 72).Unix(),
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
|
||||
t, err := token.SignedString([]byte(os.Getenv("JWT_SECRET")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.JSON(fiber.Map{"Token": t})
|
||||
}
|
5
internal/auth/models/models.go
Normal file
5
internal/auth/models/models.go
Normal file
@ -0,0 +1,5 @@
|
||||
package models
|
||||
|
||||
type AuthService interface {
|
||||
IsValidUser(user, password string) bool
|
||||
}
|
31
internal/auth/repositories/pam_repository.go
Normal file
31
internal/auth/repositories/pam_repository.go
Normal file
@ -0,0 +1,31 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/msteinert/pam"
|
||||
)
|
||||
|
||||
type PamRepository struct{}
|
||||
|
||||
func NewPamRepository() *PamRepository {
|
||||
return &PamRepository{}
|
||||
}
|
||||
|
||||
func (pr *PamRepository) IsValidUser(user, password string) bool {
|
||||
tx, err := pam.StartFunc("virteen", user, func(s pam.Style, msg string) (string, error) {
|
||||
return password, nil
|
||||
})
|
||||
if err != nil {
|
||||
panic("failed to start PAM transaction")
|
||||
}
|
||||
|
||||
err = tx.Authenticate(pam.Silent)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "authenticate: %s\n", err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
17
internal/auth/services/pam_auth_service.go
Normal file
17
internal/auth/services/pam_auth_service.go
Normal file
@ -0,0 +1,17 @@
|
||||
package services
|
||||
|
||||
import "git.cesium.pw/niku/virteen/internal/auth/repositories"
|
||||
|
||||
type PamAuthService struct {
|
||||
pam *repositories.PamRepository
|
||||
}
|
||||
|
||||
func NewPamAuthService() *PamAuthService {
|
||||
return &PamAuthService{
|
||||
pam: repositories.NewPamRepository(),
|
||||
}
|
||||
}
|
||||
|
||||
func (pas PamAuthService) IsValidUser(user, password string) bool {
|
||||
return pas.pam.IsValidUser(user, password)
|
||||
}
|
20
internal/middleware/auth.go
Normal file
20
internal/middleware/auth.go
Normal file
@ -0,0 +1,20 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
jwtware "github.com/gofiber/contrib/jwt"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func errorHandler(c *fiber.Ctx, err error) error {
|
||||
c.Status(401)
|
||||
return err
|
||||
}
|
||||
|
||||
func Protected() fiber.Handler {
|
||||
return jwtware.New(jwtware.Config{
|
||||
SigningKey: jwtware.SigningKey{Key: []byte(os.Getenv("JWT_SECRET"))},
|
||||
ErrorHandler: errorHandler,
|
||||
})
|
||||
}
|
43
internal/middleware/error_hander.go
Normal file
43
internal/middleware/error_hander.go
Normal file
@ -0,0 +1,43 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type ProblemDetails struct {
|
||||
Type string
|
||||
Title string
|
||||
Status int
|
||||
Detail string
|
||||
}
|
||||
|
||||
func ErrorHandler() fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
status := fiber.StatusInternalServerError
|
||||
c.Status(status).JSON(ProblemDetails{
|
||||
Type: fmt.Sprintf("https://httpstatuses.io/%d", status),
|
||||
Title: "Internal Server Error",
|
||||
Status: status,
|
||||
Detail: fmt.Sprint(r),
|
||||
})
|
||||
}
|
||||
}()
|
||||
|
||||
err := c.Next()
|
||||
if err != nil {
|
||||
status := c.Response().StatusCode()
|
||||
c.JSON(ProblemDetails{
|
||||
Type: fmt.Sprintf("https://httpstatuses.io/%d", status),
|
||||
Title: err.Error(),
|
||||
Status: status,
|
||||
Detail: "No further details available",
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
30
internal/podman/models/models.go
Normal file
30
internal/podman/models/models.go
Normal file
@ -0,0 +1,30 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/containers/podman/v4/pkg/domain/entities"
|
||||
)
|
||||
|
||||
type ContainerStatus int
|
||||
|
||||
const (
|
||||
StopContainer ContainerStatus = iota
|
||||
StartContainer
|
||||
RestartContainer
|
||||
)
|
||||
|
||||
type PodmanService interface {
|
||||
List() ([]entities.ListContainer, error)
|
||||
Create(ContainerBlueprint) error
|
||||
Delete(string) error
|
||||
UpdateStatus(string, string) error
|
||||
}
|
||||
|
||||
type PortMapping struct {
|
||||
ContainerPort uint16 `json:"containerPort"`
|
||||
HostPort uint16 `json:"hostPort"`
|
||||
}
|
||||
|
||||
type ContainerBlueprint struct {
|
||||
Image string `json:"image"`
|
||||
PortMapping []PortMapping `json:"portMapping"`
|
||||
}
|
42
internal/podman/podman_controller.go
Normal file
42
internal/podman/podman_controller.go
Normal file
@ -0,0 +1,42 @@
|
||||
package podman
|
||||
|
||||
import (
|
||||
"git.cesium.pw/niku/virteen/internal/podman/models"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type PodmanController struct {
|
||||
containerService *models.PodmanService
|
||||
}
|
||||
|
||||
func NewPodmanController(cs *models.PodmanService) *PodmanController {
|
||||
return &PodmanController{containerService: cs}
|
||||
}
|
||||
|
||||
func (cc *PodmanController) ListContainers(ctx *fiber.Ctx) error {
|
||||
containerList, err := (*cc.containerService).List()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.JSON(containerList)
|
||||
}
|
||||
|
||||
func (cc *PodmanController) CreateContainer(ctx *fiber.Ctx) error {
|
||||
var blueprint models.ContainerBlueprint
|
||||
if err := ctx.BodyParser(&blueprint); err != nil {
|
||||
return err
|
||||
}
|
||||
return (*cc.containerService).Create(blueprint)
|
||||
}
|
||||
|
||||
func (cc *PodmanController) UpdateContainerStatus(ctx *fiber.Ctx) error {
|
||||
name := ctx.Params("name")
|
||||
state := ctx.Query("state")
|
||||
return (*cc.containerService).UpdateStatus(name, state)
|
||||
}
|
||||
|
||||
func (cc *PodmanController) DeleteContainer(ctx *fiber.Ctx) error {
|
||||
name := ctx.Params("name")
|
||||
return (*cc.containerService).Delete(name)
|
||||
}
|
93
internal/podman/repositories/podman_repository.go
Normal file
93
internal/podman/repositories/podman_repository.go
Normal file
@ -0,0 +1,93 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.cesium.pw/niku/virteen/internal/podman/models"
|
||||
"github.com/containers/common/libnetwork/types"
|
||||
"github.com/containers/podman/v4/pkg/bindings"
|
||||
"github.com/containers/podman/v4/pkg/bindings/containers"
|
||||
"github.com/containers/podman/v4/pkg/bindings/images"
|
||||
"github.com/containers/podman/v4/pkg/domain/entities"
|
||||
"github.com/containers/podman/v4/pkg/specgen"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func newPodmanContext() (context.Context, error) {
|
||||
conn, err := bindings.NewConnection(context.Background(), "unix:///run/user/1000/podman/podman.sock")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
type PodmanRepository struct {
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (pcs *PodmanRepository) ListContainers() ([]entities.ListContainer, error) {
|
||||
return containers.List(pcs.ctx, &containers.ListOptions{})
|
||||
}
|
||||
|
||||
func (pcs *PodmanRepository) CreateContainer(bp models.ContainerBlueprint) error {
|
||||
pullQuiet := true
|
||||
_, err := images.Pull(pcs.ctx, bp.Image, &images.PullOptions{Quiet: &pullQuiet})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to pull image")
|
||||
}
|
||||
|
||||
s := specgen.NewSpecGenerator("nginx", false)
|
||||
|
||||
portMapping := make([]types.PortMapping, len(bp.PortMapping))
|
||||
for i, pm := range bp.PortMapping {
|
||||
portMapping[i] = types.PortMapping{ContainerPort: pm.ContainerPort, HostPort: pm.HostPort}
|
||||
}
|
||||
|
||||
s.ContainerNetworkConfig.PortMappings = append(s.ContainerNetworkConfig.PortMappings, portMapping...)
|
||||
|
||||
createResponse, err := containers.CreateWithSpec(pcs.ctx, s, nil)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to create spec")
|
||||
}
|
||||
|
||||
if err := containers.Start(pcs.ctx, createResponse.ID, nil); err != nil {
|
||||
return errors.Wrapf(err, "failed to start container")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pcs *PodmanRepository) UpdateContainerStatus(name string, state models.ContainerStatus) error {
|
||||
switch state {
|
||||
case models.StopContainer:
|
||||
return containers.Stop(pcs.ctx, name, nil)
|
||||
case models.StartContainer:
|
||||
return containers.Start(pcs.ctx, name, nil)
|
||||
case models.RestartContainer:
|
||||
return containers.Restart(pcs.ctx, name, nil)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (pcs *PodmanRepository) DeleteContainer(name string) error {
|
||||
err := pcs.UpdateContainerStatus(name, models.StopContainer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = containers.Remove(pcs.ctx, name, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func NewPodmanRepository() (*PodmanRepository, error) {
|
||||
ctx, err := newPodmanContext()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &PodmanRepository{ctx: ctx}, nil
|
||||
}
|
51
internal/podman/services/podman_service.go
Normal file
51
internal/podman/services/podman_service.go
Normal file
@ -0,0 +1,51 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.cesium.pw/niku/virteen/internal/podman/models"
|
||||
"git.cesium.pw/niku/virteen/internal/podman/repositories"
|
||||
"github.com/containers/podman/v4/pkg/domain/entities"
|
||||
)
|
||||
|
||||
type PodmanContainerService struct {
|
||||
podman *repositories.PodmanRepository
|
||||
}
|
||||
|
||||
func (pcs *PodmanContainerService) List() ([]entities.ListContainer, error) {
|
||||
return pcs.podman.ListContainers()
|
||||
}
|
||||
|
||||
func (pcs *PodmanContainerService) Create(blueprint models.ContainerBlueprint) error {
|
||||
return pcs.podman.CreateContainer(blueprint)
|
||||
}
|
||||
|
||||
func (pcs *PodmanContainerService) Delete(name string) error {
|
||||
return pcs.podman.DeleteContainer(name)
|
||||
}
|
||||
|
||||
func (pcs *PodmanContainerService) UpdateStatus(name, status string) error {
|
||||
var state models.ContainerStatus
|
||||
if status == "started" {
|
||||
state = models.StartContainer
|
||||
} else if status == "stopped" {
|
||||
state = models.StopContainer
|
||||
} else if status == "restarting" {
|
||||
state = models.RestartContainer
|
||||
} else {
|
||||
return fmt.Errorf("invalid state, should be started, stopped or restarting")
|
||||
}
|
||||
|
||||
return pcs.podman.UpdateContainerStatus(name, state)
|
||||
}
|
||||
|
||||
func NewPodmanContainerService() (*PodmanContainerService, error) {
|
||||
podmanRepo, err := repositories.NewPodmanRepository()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &PodmanContainerService{
|
||||
podman: podmanRepo,
|
||||
}, nil
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user