aschmelyun / fleet

Run multiple Laravel Sail websites locally with custom domain names and https
MIT License
318 stars 13 forks source link

Undefined array key "ports" #14

Open calebdw opened 1 year ago

calebdw commented 1 year ago

Hello!

I seem to be unable to add my dev site to fleet, it fails with the following error:

$ php artisan fleet:add

 What domain name would you like to use for this app? [laravel.localhost]:
 > 

 This will modify your docker-compose.yml file, do you want to back it up first? (yes/no) [no]:
 > yes

   ErrorException 

  Undefined array key "ports"

  at vendor/aschmelyun/fleet/src/Commands/FleetAddCommand.php:138
    134▕         ];
    135▕ 
    136▕         // removes port binding for our app service
    137▕         unset($yaml['services'][$heading]['ports'][0]);
  ➜ 138▕         $yaml['services'][$heading]['ports'] = array_values($yaml['services'][$heading]['ports']);
    139▕ 
    140▕         // adds the fleet network
    141▕         $yaml['networks']['fleet']['external'] = true;
    142▕ 

      +15 vendor frames 
  16  artisan:37
      Illuminate\Foundation\Console\Kernel::handle()

Note that my docker compose uses a reverse-proxy with nginx.

joseph-d commented 11 months ago

I noticed this too, it happens if you run the add command twice and then you end up with something like, ports { } in your yaml. If you just restore the ports section from your backup of docker-compose.yaml then run add again it should work.

calebdw commented 11 months ago

@joseph-d, this happened the first time running the command

joseph-d commented 11 months ago

Well, basically, the fleet:add script takes away whatever is in the first position of the list of ports. I guess this is because in the default Laravel docker stub (https://github.com/laravel/sail/blob/1.x/stubs/docker-compose.stub) it looks like this:

        ports:
            - '${APP_PORT:-80}:80'
            - '${VITE_PORT:-5173}:${VITE_PORT:-5173}'

So the unset($yaml['services'][$heading]['ports'][0]); line of fleet:add is just changing it to:

        ports:
            - '${APP_PORT:-80}:80'
            - '${VITE_PORT:-5173}:${VITE_PORT:-5173}'

But then if you run it a second time, or if it already just had one item in the list to begin with, then it's going to end up as:

        ports: { }

..and this seems to be the thing that causes the Undefined array key "ports" error.

So if your docker-compose actually just has ports: { } in it, or it's otherwise empty, you can just remove it completely and then the command will run without that error.

calebdw commented 11 months ago

Hmmm...I'll take another look---however, this feels very brittle and should probably still be fixed

joseph-d commented 11 months ago

Agreed, it would probably be better if the logic identified the specific binding to remove rather than just assuming that the first one has to go, regardless of what it is, and also the way of forming the yaml when the list is empty needs to be looked at. I think I can provably do a PR to address all of this but let's see if you are still getting the error after checking what I mentioned above first.

calebdw commented 11 months ago

Well the command completes, but is unable to start the network---likely because I'm using a reverse proxy:

version: "3.9"

services:
  app:
    container_name: "app"
    build:
      context: ./docker
      args:
        - DOCKER_IMAGE_VERSION=${DOCKER_IMAGE_VERSION:-latest}
    extra_hosts:
      - "host.docker.internal:host-gateway"
    restart: unless-stopped
    expose:
      - "9000"
    environment:
      WORKDIR: ${DOCKER_WORKDIR}
      USER_ID: ${DOCKER_USER_ID}
      USER: ${WWWUSER}
      LARAVEL_SAIL: 1
      SERVICE_NAME: ${APP_SERVICE}
      SERVICE_TAGS: dev
    depends_on:
      - webserver
    volumes:
      - "./:${DOCKER_WORKDIR}"
      - "./docker/php/php.ini:/usr/local/etc/php/conf.d/php.ini"
      - "${DOCKER_COMPOSER_HOME}/auth.json:/home/${WWWUSER}/.composer/auth.json"
    networks:
      - app-network

  webserver:
    container_name: "nginx"
    image: nginx:alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "./:${DOCKER_WORKDIR}"
      - "./docker/nginx/conf.d/:/etc/nginx/conf.d/"
      - "./docker/nginx/nginx_boilerplate/:/etc/nginx/nginx_boilerplate/"
      - "./docker/nginx/certs/:/etc/nginx/certs/"
    networks:
      - app-network

networks:
  app-network:
    driver: bridge
joseph-d commented 11 months ago

I'm not sure if it's even supposed to work with a docker config that has been modified to this extent. Looking at the FleetAddCommand.php it would seem as though it expects a virgin docker-compose.yaml from Laravel Sail.

The first thing I can see that's obviously preventing it from working in your config, though, is the absence of references to the fleet network provided by the traefik container. So if you go ahead and add - fleet under the network of your webserver container then that will b a step in the right direction. Also, completely remove the ports section of your webserver container too then add the following labels also under webserver:

labels:
    - traefik.http.routers.webserver.rule=Host(`webserver.localhost`)
    - traefik.http.services.webserver.loadbalancer.server.port=80
    - traefik.docker.network=fleet

Finally, update your networks at the end of that file to be like this:

networks:
    app-network:
        driver: bridge
    fleet:
        external: true

The add script also seems to suggest that APP_PORT=8081 (and then an incremental port number for each project) needs to be added in the .env but I've found that it seems to work without this addition so I'm not sure why it's there.

Then, as long as your fleet container (the traefik image) is running it should work. You can check and make sure your config (ie: the stuff put under labels) has been picked up by going to the traefik dashboard at http://localhost:8080/dashboard/#/http/services

But that said, I'm really only guessing here because I think fleet was just designed to work with a standard sail installation which uses php artisan serve the fact that you've updated your docker config to include nginx is probably the reason why the add script didn't run properly, and so much of the necessary config appears to have been missed out. It might work by making the changes I suggested above, but then a more sensible option might just be to start again with a fresh installation of sail, get that working with fleet first and then incrementally make your nginx additions once you've got the basic setup working.