fix(dev): address CLI review feedback
This commit is contained in:
18 files changed
+307
-160
No files matched your search
@@ -12,6 +12,8 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const defaultDevelopmentEncryptionKey = "cdca265c289df60f6605a2fc9c43cfa8eb6efdc98b203cad2c3f2970aca09fbe"
|
||||
|
||||
type Container struct {
|
||||
ID string
|
||||
Name string
|
||||
@@ -82,8 +84,12 @@ func List() ([]Container, error) {
|
||||
}
|
||||
|
||||
func FindFreePort(start int) int {
|
||||
if start > 65535 {
|
||||
return 0
|
||||
}
|
||||
used := usedDockerPorts()
|
||||
for port := start; ; port++ {
|
||||
start = max(start, 1)
|
||||
for port := start; port <= 65535; port++ {
|
||||
if used[port] {
|
||||
continue
|
||||
}
|
||||
@@ -93,36 +99,43 @@ func FindFreePort(start int) int {
|
||||
return port
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func usedDockerPorts() map[int]bool {
|
||||
used := make(map[int]bool)
|
||||
out, err := exec.Command("docker", "ps", "--format", "{{.Ports}}").Output()
|
||||
if err != nil {
|
||||
return used
|
||||
return make(map[int]bool)
|
||||
}
|
||||
for _, mapping := range strings.Split(string(out), ",") {
|
||||
left, _, found := strings.Cut(mapping, "->")
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
colon := strings.LastIndex(left, ":")
|
||||
if colon < 0 {
|
||||
continue
|
||||
}
|
||||
ports := strings.SplitN(strings.TrimSpace(left[colon+1:]), "-", 2)
|
||||
first, err := strconv.Atoi(ports[0])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
last := first
|
||||
if len(ports) == 2 {
|
||||
if parsed, err := strconv.Atoi(ports[1]); err == nil {
|
||||
last = parsed
|
||||
return parseUsedDockerPorts(out)
|
||||
}
|
||||
|
||||
func parseUsedDockerPorts(out []byte) map[int]bool {
|
||||
used := make(map[int]bool)
|
||||
for _, line := range strings.Split(string(out), "\n") {
|
||||
for _, mapping := range strings.Split(line, ",") {
|
||||
left, _, found := strings.Cut(mapping, "->")
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
colon := strings.LastIndex(left, ":")
|
||||
if colon < 0 {
|
||||
continue
|
||||
}
|
||||
ports := strings.SplitN(strings.TrimSpace(left[colon+1:]), "-", 2)
|
||||
first, err := strconv.Atoi(ports[0])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
last := first
|
||||
if len(ports) == 2 {
|
||||
if parsed, err := strconv.Atoi(ports[1]); err == nil {
|
||||
last = parsed
|
||||
}
|
||||
}
|
||||
for port := first; port <= last; port++ {
|
||||
used[port] = true
|
||||
}
|
||||
}
|
||||
for port := first; port <= last; port++ {
|
||||
used[port] = true
|
||||
}
|
||||
}
|
||||
return used
|
||||
@@ -162,8 +175,12 @@ func Start(opts StartOptions) error {
|
||||
|
||||
_ = exec.Command("docker", "rm", "-f", opts.Name).Run()
|
||||
|
||||
encryptionKey := os.Getenv("HOMARR_DEV_SECRET_ENCRYPTION_KEY")
|
||||
if encryptionKey == "" {
|
||||
encryptionKey = defaultDevelopmentEncryptionKey
|
||||
}
|
||||
args := []string{"run", "--rm", "--name", opts.Name,
|
||||
"-e", "SECRET_ENCRYPTION_KEY=cdca265c289df60f6605a2fc9c43cfa8eb6efdc98b203cad2c3f2970aca09fbe",
|
||||
"-e", "SECRET_ENCRYPTION_KEY=" + encryptionKey,
|
||||
}
|
||||
if opts.Platform != "" {
|
||||
args = append(args, "--platform", opts.Platform)
|
||||
|
||||
@@ -25,3 +25,18 @@ func TestIsPortConflict(t *testing.T) {
|
||||
t.Fatal("manifest error classified as port conflict")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseUsedDockerPortsPreservesLineBoundaries(t *testing.T) {
|
||||
ports := parseUsedDockerPorts([]byte("6379/tcp\n0.0.0.0:7575->7575/tcp, [::]:7575->7575/tcp\n0.0.0.0:8000-8002->8000-8002/tcp\n"))
|
||||
for _, port := range []int{7575, 8000, 8001, 8002} {
|
||||
if !ports[port] {
|
||||
t.Fatalf("port %d was not detected", port)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindFreePortRejectsOutOfRangeStart(t *testing.T) {
|
||||
if port := FindFreePort(65536); port != 0 {
|
||||
t.Fatalf("port = %d, want 0", port)
|
||||
}
|
||||
}
|
||||
@@ -56,8 +56,8 @@ func parseImageList(output []byte) ([]Image, error) {
|
||||
return images, nil
|
||||
}
|
||||
|
||||
func ListLocalImages() ([]Image, error) {
|
||||
out, err := exec.Command("docker", "image", "ls", "--filter", "reference=homarr:*", "--format", "{{json .}}").Output()
|
||||
func ListLocalImages(ctx context.Context) ([]Image, error) {
|
||||
out, err := exec.CommandContext(ctx, "docker", "image", "ls", "--filter", "reference=homarr:*", "--format", "{{json .}}").Output()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list local Homarr images: %w", err)
|
||||
}
|
||||
@@ -65,7 +65,7 @@ func ListLocalImages() ([]Image, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
labelsByImage, err := inspectImageLabels(images)
|
||||
labelsByImage, err := inspectImageLabels(ctx, images)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -77,7 +77,7 @@ func ListLocalImages() ([]Image, error) {
|
||||
return images, nil
|
||||
}
|
||||
|
||||
func inspectImageLabels(images []Image) ([]map[string]string, error) {
|
||||
func inspectImageLabels(ctx context.Context, images []Image) ([]map[string]string, error) {
|
||||
if len(images) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -85,7 +85,7 @@ func inspectImageLabels(images []Image) ([]map[string]string, error) {
|
||||
for _, image := range images {
|
||||
args = append(args, image.Reference())
|
||||
}
|
||||
out, err := exec.Command("docker", args...).Output()
|
||||
out, err := exec.CommandContext(ctx, "docker", args...).Output()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("inspect local Homarr images: %w", err)
|
||||
}
|
||||
@@ -135,10 +135,6 @@ func BuildCommand(ctx context.Context, options BuildOptions) (*exec.Cmd, error)
|
||||
return command, nil
|
||||
}
|
||||
|
||||
func Build(options BuildOptions) error {
|
||||
return BuildContext(context.Background(), options, os.Stdout, os.Stderr)
|
||||
}
|
||||
|
||||
func BuildContext(ctx context.Context, options BuildOptions, stdout, stderr io.Writer) error {
|
||||
command, err := BuildCommand(ctx, options)
|
||||
if err != nil {
|
||||
|
||||
@@ -17,7 +17,7 @@ func TestParseImageListFiltersHomarrTags(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInspectImageLabelsWithNoImages(t *testing.T) {
|
||||
labels, err := inspectImageLabels(nil)
|
||||
labels, err := inspectImageLabels(t.Context(), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -53,7 +53,7 @@ func TestListLocalImagesIntegration(t *testing.T) {
|
||||
if wantTag == "" {
|
||||
t.Skip("set HOMARR_TEST_LOCAL_IMAGE to a local homarr image tag")
|
||||
}
|
||||
images, err := ListLocalImages()
|
||||
images, err := ListLocalImages(t.Context())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user