Make sure port is actually free and show what port was used.
This commit is contained in:
64
cmd/hoster_cli/commands/checkUpdates.go
Normal file
64
cmd/hoster_cli/commands/checkUpdates.go
Normal file
@ -0,0 +1,64 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"hoster/internals/conf"
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
dc "github.com/compose-spec/compose-go/v2/cli"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func CmdCheckUpdated(c *cli.Context) error {
|
||||
projectRoot := conf.App.MustString("projects.root")
|
||||
|
||||
composeFiles := []string{}
|
||||
|
||||
// Find compose files recursively
|
||||
err := filepath.WalkDir(projectRoot, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
// Ignore errors on purpose.
|
||||
return nil
|
||||
}
|
||||
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
fileName := d.Name()
|
||||
|
||||
if strings.HasPrefix(fileName, "docker-compose") && (strings.HasSuffix(fileName, ".yml") || strings.HasSuffix(fileName, ".yaml")) {
|
||||
composeFiles = append(composeFiles, path)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
composeFilePath := composeFiles[0]
|
||||
|
||||
options, err := dc.NewProjectOptions(
|
||||
[]string{composeFilePath},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
project, err := dc.ProjectFromOptions(context.Background(), options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for srvName, srv := range project.AllServices() {
|
||||
fmt.Printf("%s => %s\n", srvName, srv.Image)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
117
cmd/hoster_cli/commands/new.go
Normal file
117
cmd/hoster_cli/commands/new.go
Normal file
@ -0,0 +1,117 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hoster/internals/conf"
|
||||
"hoster/internals/helpers"
|
||||
"hoster/internals/templates"
|
||||
"hoster/internals/types"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
|
||||
"github.com/gookit/goutil/fsutil"
|
||||
"github.com/gookit/goutil/sysutil/cmdr"
|
||||
"github.com/kataras/golog"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func CmdNew(c *cli.Context) error {
|
||||
name := c.String("name")
|
||||
domain := c.String("domain")
|
||||
projectRoot := conf.App.MustString("projects.root")
|
||||
sitesAvail := conf.App.MustString("nginx.sitesAvailable")
|
||||
sitesEnabled := conf.App.MustString("nginx.sitesEnabled")
|
||||
nginxFile := filepath.Join(sitesAvail, name)
|
||||
|
||||
projectFolder := filepath.Join(projectRoot, name)
|
||||
|
||||
existingProjects, err := helpers.FindHostConfigs(sitesAvail)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
usedPorts := []int{}
|
||||
for _, project := range existingProjects {
|
||||
usedPorts = append(usedPorts, project.InternalPort)
|
||||
}
|
||||
|
||||
port := conf.App.Int("nginx.portRangeStart")
|
||||
freePort := 0
|
||||
for port <= conf.App.Int("nginx.portRangeEnd") {
|
||||
if !slices.Contains(usedPorts, port) && helpers.IsFreePort(port) {
|
||||
freePort = port
|
||||
break
|
||||
}
|
||||
port++
|
||||
}
|
||||
|
||||
if freePort == 0 {
|
||||
return fmt.Errorf("no free port available")
|
||||
}
|
||||
|
||||
if !fsutil.IsDir(projectFolder) {
|
||||
golog.Infof("Creating project folder '%s'", projectFolder)
|
||||
err := os.MkdirAll(projectFolder, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if fsutil.IsFile(nginxFile) {
|
||||
return fmt.Errorf("the nginx config file '%s' already exists", nginxFile)
|
||||
}
|
||||
|
||||
// Set owner and group of project folder
|
||||
err = os.Chown(projectFolder, conf.App.Int("projects.owner"), conf.App.Int("projects.group"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
project := types.Project{
|
||||
Name: name,
|
||||
Domain: domain,
|
||||
InternalPort: freePort,
|
||||
}
|
||||
|
||||
// Create nginx config file
|
||||
err = templates.Execute("nginx", nginxFile, project)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Symlink nginx config file
|
||||
err = os.Symlink(nginxFile, filepath.Join(sitesEnabled, name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Execute certbot command: "systemctl stop nginx && sudo certbot certonly --standalone --preferred-challenges http -d {domain} && sudo systemctl start nginx"
|
||||
|
||||
com := cmdr.NewCmd("systemctl").
|
||||
WithArgs([]string{"stop", "nginx"})
|
||||
|
||||
_, err = com.Output()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
com = cmdr.NewCmd("certbot").
|
||||
WithArgs([]string{"certonly", "--standalone", "--preferred-challenges", "http", "-d", domain})
|
||||
_, err = com.Output()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
com = cmdr.NewCmd("systemctl").
|
||||
WithArgs([]string{"start", "nginx"})
|
||||
_, err = com.Output()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
golog.Infof("Project successfully '%s' created. Uses port %d.", name, freePort)
|
||||
|
||||
return nil
|
||||
}
|
@ -1,19 +1,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hoster/cmd/hoster_cli/commands"
|
||||
"hoster/internals/conf"
|
||||
"hoster/internals/helpers"
|
||||
"hoster/internals/templates"
|
||||
"hoster/internals/types"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gookit/goutil/fsutil"
|
||||
"github.com/gookit/goutil/sysutil/cmdr"
|
||||
"github.com/kataras/golog"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -22,7 +15,7 @@ func main() {
|
||||
app := &cli.App{
|
||||
Name: "hoster",
|
||||
Usage: "Hoster",
|
||||
Version: "1.0.0",
|
||||
Version: "1.0.1",
|
||||
Commands: []*cli.Command{
|
||||
{
|
||||
Name: "new",
|
||||
@ -41,96 +34,12 @@ func main() {
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
name := c.String("name")
|
||||
domain := c.String("domain")
|
||||
projectRoot := conf.App.MustString("projects.root")
|
||||
sitesAvail := conf.App.MustString("nginx.sitesAvailable")
|
||||
sitesEnabled := conf.App.MustString("nginx.sitesEnabled")
|
||||
nginxFile := filepath.Join(sitesAvail, name)
|
||||
|
||||
projectFolder := filepath.Join(projectRoot, name)
|
||||
|
||||
existingProjects, err := helpers.FindHostConfigs(sitesAvail)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
usedPorts := []int{}
|
||||
for _, project := range existingProjects {
|
||||
usedPorts = append(usedPorts, project.InternalPort)
|
||||
}
|
||||
|
||||
port := conf.App.Int("nginx.portRangeStart")
|
||||
freePort := 0
|
||||
for port <= conf.App.Int("nginx.portRangeEnd") {
|
||||
if !slices.Contains(usedPorts, port) {
|
||||
freePort = port
|
||||
break
|
||||
}
|
||||
port++
|
||||
}
|
||||
|
||||
if freePort == 0 {
|
||||
return fmt.Errorf("no free port available")
|
||||
}
|
||||
|
||||
if !fsutil.IsDir(projectFolder) {
|
||||
golog.Infof("Creating project folder '%s'", projectFolder)
|
||||
err := os.MkdirAll(projectFolder, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if fsutil.IsFile(nginxFile) {
|
||||
return fmt.Errorf("the nginx config file '%s' already exists", nginxFile)
|
||||
}
|
||||
|
||||
// Set owner and group of project folder
|
||||
err = os.Chown(projectFolder, conf.App.Int("projects.owner"), conf.App.Int("projects.group"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
project := types.Project{
|
||||
Name: name,
|
||||
Domain: domain,
|
||||
InternalPort: freePort,
|
||||
}
|
||||
|
||||
// Create nginx config file
|
||||
err = templates.Execute("nginx", nginxFile, project)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Symlink nginx config file
|
||||
err = os.Symlink(nginxFile, filepath.Join(sitesEnabled, name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Execute certbot command: "systemctl stop nginx && sudo certbot certonly --standalone --preferred-challenges http -d {domain} && sudo systemctl start nginx"
|
||||
|
||||
com := cmdr.NewCmd("systemctl").
|
||||
WithArgs([]string{"stop", "nginx"})
|
||||
out := com.SafeOutput()
|
||||
fmt.Println(out)
|
||||
|
||||
com = cmdr.NewCmd("certbot").
|
||||
WithArgs([]string{"certonly", "--standalone", "--preferred-challenges", "http", "-d", domain})
|
||||
out = com.SafeOutput()
|
||||
fmt.Println(out)
|
||||
|
||||
com = cmdr.NewCmd("systemctl").
|
||||
WithArgs([]string{"start", "nginx"})
|
||||
out = com.SafeOutput()
|
||||
fmt.Println(out)
|
||||
|
||||
return nil
|
||||
},
|
||||
Action: commands.CmdNew,
|
||||
},
|
||||
{
|
||||
Name: "check",
|
||||
Usage: "Check if newer versions of docker images are available",
|
||||
Action: commands.CmdCheckUpdated,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
Reference in New Issue
Block a user