Skip to content

Commit

Permalink
Refactor: Enhance container management and UI components
Browse files Browse the repository at this point in the history
- Add TraefikEntryPoint field to ContainerCommandParams struct
- Update fetch command to handle Traefik entry point
- Refactor container list and creation UI components
- Improve error handling and state management in hyperscript
- Update _hyperscript and htmx libraries to latest versions
  • Loading branch information
bnema committed Sep 16, 2024
1 parent 11bec3a commit e9b201c
Show file tree
Hide file tree
Showing 13 changed files with 184 additions and 160 deletions.
2 changes: 1 addition & 1 deletion internal/cli/cmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func NewDeployCommand(a *cli.App) *cobra.Command {
},
}

deployCmd.Flags().StringVarP(&port, "port", "p", "", "Port mapping for the container")
deployCmd.Flags().StringVarP(&port, "port", "p", "", "Port mapping for the container and Traefik entry point")
deployCmd.Flags().StringVarP(&targetDomain, "target", "t", "", "Target domain for Traefik")

return deployCmd
Expand Down
65 changes: 56 additions & 9 deletions internal/common/initconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"path/filepath"
"runtime"
"strings"

"github.com/bnema/gordon/pkg/docker"
"github.com/bnema/gordon/pkg/parser"
Expand Down Expand Up @@ -68,21 +69,50 @@ var (
)

func getConfigDir() (string, error) {
var configDir string

if isWSL() {
// Use XDG_CONFIG_HOME for WSL
if xdgConfigHome := os.Getenv("XDG_CONFIG_HOME"); xdgConfigHome != "" {
configDir = filepath.Join(xdgConfigHome, "Gordon")
} else {
// If XDG_CONFIG_HOME is not set, fall back to default locations
homeDir, err := os.UserHomeDir()
if err != nil {
// If we can't get the home directory, use a fallback
homeDir = os.TempDir() // Use the system's temp directory as a fallback
fmt.Printf("Warning: Unable to determine home directory. Using temp directory: %s\n", homeDir)
}

configDir = filepath.Join(homeDir, ".config", "Gordon")

}

return configDir, nil
}

if docker.IsRunningInContainer() {
return "/.", nil
}

// Get the user's home directory for non-container environments
homeDir, err := os.UserHomeDir()
if err != nil {
return "", fmt.Errorf("error getting user's home directory: %w", err)
}
// Check for XDG_CONFIG_HOME first
if xdgConfigHome := os.Getenv("XDG_CONFIG_HOME"); xdgConfigHome != "" {
configDir = filepath.Join(xdgConfigHome, "Gordon")
} else {
// If XDG_CONFIG_HOME is not set, fall back to default locations
homeDir, err := os.UserHomeDir()
if err != nil {
// If we can't get the home directory, use a fallback
homeDir = os.TempDir() // Use the system's temp directory as a fallback
fmt.Printf("Warning: Unable to determine home directory. Using temp directory: %s\n", homeDir)
}

// Select the configuration directory based on the OS
configDir := filepath.Join(homeDir, ".config", "Gordon")
if runtime.GOOS == "windows" {
configDir = filepath.Join(homeDir, "AppData", "Roaming", "Gordon")
// Select the configuration directory based on the OS
if runtime.GOOS == "windows" {
configDir = filepath.Join(homeDir, "AppData", "Roaming", "Gordon")
} else {
configDir = filepath.Join(homeDir, ".config", "Gordon")
}
}

return configDir, nil
Expand Down Expand Up @@ -201,3 +231,20 @@ func (c *Config) GetBackendURL() string {
func (c *Config) SetToken(token string) {
c.General.Token = token
}

func isWSL() bool {
// Check for /proc/version file
if _, err := os.Stat("/proc/version"); err == nil {
content, err := os.ReadFile("/proc/version")
if err == nil && strings.Contains(strings.ToLower(string(content)), "microsoft") {
return true
}
}

// Check for WSL-specific environment variable
if os.Getenv("WSL_DISTRO_NAME") != "" {
return true
}

return false
}
45 changes: 30 additions & 15 deletions internal/httpserve/handler/containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package handler
import (
"fmt"
"net/http"
"strings"
"sync"
"time"

Expand Down Expand Up @@ -61,13 +62,14 @@ type HumanReadableContainerImage struct {

type HumanReadableContainer struct {
*types.Container
Name string
Ports []string
ShortID string
CreatedStr string
SizeStr string
UpSince string
StateColor string
Name string
Ports []string
ShortID string
CreatedStr string
SizeStr string
UpSince string
StateColor string
TraefikEntryPoint string
}

// renderHTML is a generalized function to render HTML
Expand Down Expand Up @@ -162,9 +164,11 @@ func ContainerManagerComponent(c echo.Context, a *server.App) error {
for _, container := range containers {
localContainer := container // Make a local copy
sizeStr := humanize.BytesToReadableSize(container.SizeRw)
stateColor := "green"
stateColor := "blue"

if container.State == "exited" {
if container.State == "running" {
stateColor = "green"
} else if container.State == "exited" {
stateColor = "red"
}
var ports []string
Expand All @@ -176,12 +180,14 @@ func ContainerManagerComponent(c echo.Context, a *server.App) error {
for _, name := range container.Names {
name = name[1:]
humanReadableContainers = append(humanReadableContainers, HumanReadableContainer{
Container: &localContainer,
SizeStr: sizeStr,
UpSince: humanize.TimeAgo(time.Unix(container.Created, 0)),
StateColor: stateColor,
Name: name,
Ports: ports,
Container: &localContainer,
SizeStr: sizeStr,
UpSince: humanize.TimeAgo(time.Unix(container.Created, 0)),
StateColor: stateColor,
Name: name,
Ports: ports,
TraefikEntryPoint: extractTraefikEntryPoint(container.Labels),
CreatedStr: humanize.TimeAgo(time.Unix(container.Created, 0)),
})
}
}
Expand All @@ -197,6 +203,15 @@ func ContainerManagerComponent(c echo.Context, a *server.App) error {
return renderHTML(c, a, "html/fragments", "containerlist.gohtml", data)
}

func extractTraefikEntryPoint(labels map[string]string) string {
for key, value := range labels {
if strings.HasSuffix(key, ".loadbalancer.server.port") {
return value
}
}
return ""
}

// ContainerManagerDelete handles the /container-manager/delete route
func ContainerManagerDelete(c echo.Context, a *server.App) error {
err := docker.RemoveContainer(c.Param("ID"))
Expand Down
23 changes: 12 additions & 11 deletions internal/templating/cmdparams/fromcli.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,20 @@ func FromPayloadStructToCmdParams(ppl *common.DeployPayload, a *server.App, imag
containerDomain := strings.Join(hostParts[1:], ".")

params := docker.ContainerCommandParams{
IsSSL: protocol == "https",
ContainerName: containerSubdomain,
ServiceName: containerSubdomain,
Domain: containerDomain,
ImageName: ppl.ImageName,
ImageID: imageID,
Restart: "always",
Volumes: volumeSlice,
Environment: environmentSlice,
Network: a.Config.ContainerEngine.Network,
IsSSL: protocol == "https",
ContainerName: containerSubdomain,
ServiceName: containerSubdomain,
Domain: containerDomain,
ImageName: ppl.ImageName,
ImageID: imageID,
Restart: "always",
Volumes: volumeSlice,
Environment: environmentSlice,
Network: a.Config.ContainerEngine.Network,
TraefikEntryPoint: ppl.Port,
}

err = CreateTraefikLabels(&params, ppl.Port, a)
err = CreateTraefikLabels(&params, params.TraefikEntryPoint, a)
if err != nil {
return docker.ContainerCommandParams{}, fmt.Errorf("error creating Traefik labels: %w", err)
}
Expand Down
38 changes: 21 additions & 17 deletions internal/templating/cmdparams/frominputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,34 @@ import (
func FromInputsToCmdParams(inputs map[string]string, a *server.App) (docker.ContainerCommandParams, error) {
volumeSlice := ParseVolumeSlice(inputs["volumes"])
environmentSlice := ParseEnvironmentSlice(inputs["environment_variables"])
portMappings, err := ParsePortMappingsSlice(inputs["ports"])

// Extract the container port (right side of the mapping)
containerPort := extractContainerPort(portMappings)
var portMappings []docker.PortMapping
var err error

if err != nil {
return docker.ContainerCommandParams{}, err
// Parse port mappings if provided
if inputs["ports"] != "" {
portMappings, err = ParsePortMappingsSlice(inputs["ports"])
if err != nil {
return docker.ContainerCommandParams{}, err
}
}

params := docker.ContainerCommandParams{
IsSSL: inputs["container_protocol"] == "https",
ContainerName: inputs["container_name"],
ServiceName: inputs["container_subdomain"],
Domain: inputs["container_domain"],
ImageName: inputs["image_name"],
ImageID: inputs["image_id"],
Restart: inputs["restart"],
Volumes: volumeSlice,
Environment: environmentSlice,
Network: a.Config.ContainerEngine.Network,
PortMappings: portMappings,
IsSSL: inputs["container_protocol"] == "https",
ContainerName: inputs["container_name"],
ServiceName: inputs["container_subdomain"],
Domain: inputs["container_domain"],
ImageName: inputs["image_name"],
ImageID: inputs["image_id"],
Restart: inputs["restart"],
TraefikEntryPoint: inputs["traefik_entry_point"],
Volumes: volumeSlice,
Environment: environmentSlice,
Network: a.Config.ContainerEngine.Network,
PortMappings: portMappings,
}

err = CreateTraefikLabels(&params, containerPort, a)
err = CreateTraefikLabels(&params, params.TraefikEntryPoint, a)
if err != nil {
return docker.ContainerCommandParams{}, fmt.Errorf("error creating Traefik labels: %w", err)
}
Expand Down
4 changes: 4 additions & 0 deletions internal/templating/cmdparams/parsing.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ func ParseEnvironmentSlice(environmentStr string) []string {

// ParsePortMappingsSlice parses the port mappings string into a slice and a struct.
func ParsePortMappingsSlice(portMappingsStr string) ([]docker.PortMapping, error) {
if portMappingsStr == "" {
return []docker.PortMapping{}, nil
}

portMappingsSliceRaw := strings.Split(portMappingsStr, ",")
portMappingsSlice := make([]string, len(portMappingsSliceRaw))
for i, spec := range portMappingsSliceRaw {
Expand Down
6 changes: 3 additions & 3 deletions internal/templating/cmdparams/traefik.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
)

// CreateTraefikLabels creates Traefik labels based on the provided parameters.
func CreateTraefikLabels(params *docker.ContainerCommandParams, portEntryPoint string, a *server.App) error {
func CreateTraefikLabels(params *docker.ContainerCommandParams, containerPort string, a *server.App) error {
entryPoint := a.Config.Traefik.EntryPoint
if params.IsSSL {
entryPoint = a.Config.Traefik.SecureEntryPoint
Expand All @@ -21,12 +21,12 @@ func CreateTraefikLabels(params *docker.ContainerCommandParams, portEntryPoint s
"traefik.enable=true",
fmt.Sprintf("%s.rule=Host(`%s.%s`)", baseRouter, params.ServiceName, params.Domain),
fmt.Sprintf("%s.entrypoints=%s", baseRouter, entryPoint),
fmt.Sprintf("%s.loadbalancer.server.port=%s", baseService, portEntryPoint),
fmt.Sprintf("%s.loadbalancer.server.port=%s", baseService, containerPort),
}

resolver := a.Config.Traefik.Resolver
if resolver == "" {
fmt.Errorf("resolver not set in config")
return fmt.Errorf("resolver not set in config")
}

if params.IsSSL {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<tr class="text-center text-gray-400">
<th>{{index .Lang "manager" "content" "thName"}}</th>
<th>{{index .Lang "manager" "content" "thPorts"}}</th>
<th>{{index .Lang "manager" "content" "thEntryPoint"}}</th>
<th>{{index .Lang "manager" "content" "thStatus"}}</th>
<th>{{index .Lang "manager" "content" "thState"}}</th>
<th>{{index .Lang "manager" "content" "thActions"}}</th>
Expand All @@ -26,6 +27,7 @@
>
</td>
<td class="port-container">{{ .Ports }}</td>
<td class="entrypoint-container">{{.TraefikEntryPoint}}</td>
<td class="status-container">{{.Status}} (Up since {{.UpSince}})</td>
<td style="color: {{.StateColor}};">{{.State}}</td>
<td>
Expand Down
Loading

0 comments on commit e9b201c

Please sign in to comment.