nginx / unit

NGINX Unit - universal web app server - a lightweight and versatile open source server that simplifies the application stack by natively executing application code across eight different programming language runtimes.
https://unit.nginx.org
Apache License 2.0
5.25k stars 322 forks source link

Query control API to show the loaded modules #1343

Open lcrilly opened 5 days ago

lcrilly commented 5 days ago

This feature extends the /status Control API endpoint to include the currently loaded modules.

Currently, the available modules are loaded by the discovery process at startup and logged to the Unit log file.

2024/06/26 09:36:16 [info] 55991#2488927 discovery started
2024/06/26 09:36:16 [notice] 55991#2488927 module: php 8.3.4 "/var/lib/unit/modules/php.unit.so"
2024/06/26 09:36:16 [notice] 55991#2488927 module: python 3.12.2 "/var/lib/unit/modules/python3.unit.so"
2024/06/26 09:36:16 [notice] 55991#2488927 module: wasm-wasi-component 0.1 "/var/lib/unit/modules/wasm_wasi_component.unit.so"
2024/06/26 09:36:17 [info] 55990#2488925 controller started

This information is already transferred to the Control API because attempts to configure an application with an unloaded "type" fails with a suitable error, e.g.

$ echo '{"bogus":{"type":"cobol"}}' | unitc /config/applications
{
  "error": "Invalid configuration.",
  "detail": "The module to run \"cobol\" is not found among the available application modules."
}

It would be much more convenient if the loaded modules information was also queryable at runtime. So the /status endpoint would return something like:

{
  "modules": [
    "php 8.3.4",
    "python 3.12.2",
    "wasm-wasi-component 0.1"
  ],
  "connections": { … },
  "requests": { … },
  "applications": { … }
}

This would provide a more convenient way of checking what language modules are loaded. It would also benefit command line tools that auto-configure Unit to serve applications.

ac000 commented 5 days ago

The language modules and versions are handily stored in the languages array member of the nxt_runtime_t structure...

We have access to the following information

typedef struct {                                                                
    nxt_app_type_t            type;                                             
    u_char                    *version;                                         
    char                      *file;                                            
    nxt_app_module_t          *module;                                          
    nxt_array_t               *mounts;    /* of nxt_fs_mount_t */               
} nxt_app_lang_module_t;

Would showing the path (e.g /opt/unit/modules/python.unit.so) to the module be useful? Might be handy for debugging to make sure it's loading the modules you expect it to...

avahahn commented 4 days ago

Would showing the path (e.g /opt/unit/modules/python.unit.so) to the module be useful?

I have wished for this information several times in fact! :) I think having this will better enable our users to roll their own docker images and to compile Unit for themselves.

lcrilly commented 4 days ago

Given that we have the type and version as separate entities in the struct, it makes sense to have an array of objects instead of the simple naive array I was envisaging.

I wasn't sure about the information disclosure of the path. And I didn't have a use case for it. But now I think about it, knowing exactly which module is loaded seems like a good idea.

ac000 commented 3 days ago

I have an initial version that will return something like

{
    "modules": {
        "python": {
            "version": "3.12.2",
            "lib": "/opt/unit/modules/python.unit.so"
        },
        "wasm": {
            "version": "0.1",
            "lib": "/opt/unit/modules/wasm.unit.so"
        },
        "wasm-wasi-component": {
            "version": "0.1",
            "lib": "/opt/unit/modules/wasm_wasi_component.unit.so"
        }
    },
...
}
lcrilly commented 7 hours ago

I look forward to trying it :)

I suggested an array of objects because we may find multiple versions of the same language module installed (especially for PHP and Python).

ac000 commented 3 hours ago

Are you saying you want the JSON formatted differently?

Currently it's modelled on the "applications" section

    "applications": {
        "luw-echo-request": {
            "processes": {
                "running": 1,
                "starting": 0,
                "idle": 1
            },

            "requests": {
                "active": 0
            }
        },

        "luw-upload-reflector": {
            "processes": {
                "running": 1,
                "starting": 0,
                "idle": 1
            },

            "requests": {
                "active": 0
            }
        },

        "echo-request-raw": {
            "processes": {
                "running": 1,
                "starting": 0,
                "idle": 1
            },

            "requests": {
                "active": 0
            }
        },

        "rust-echo-request": {
            "processes": {
                "running": 1,
                "starting": 0,
                "idle": 1
            },

            "requests": {
                "active": 0
            }
        },

        "rust-upload-reflector": {
            "processes": {
                "running": 1,
                "starting": 0,
                "idle": 1
            },

            "requests": {
                "active": 0
            }
        },

        "rust-hello-world": {
            "processes": {
                "running": 1,
                "starting": 0,
                "idle": 1
            },

            "requests": {
                "active": 0
            }
        },

        "sc-test": {
            "processes": {
                "running": 1,
                "starting": 0,
                "idle": 1
            },

            "requests": {
                "active": 0
            }
        },

        "large-upload": {
            "processes": {
                "running": 1,
                "starting": 0,
                "idle": 1
            },

            "requests": {
                "active": 0
            }
        },

        "rust-large-upload": {
            "processes": {
                "running": 1,
                "starting": 0,
                "idle": 1
            },

            "requests": {
                "active": 0
            }
        }
    }
callahad commented 1 hour ago

I think Liam probably means:

{
    "modules": {
        "python": [
            {
                "version": "3.12.2",
                "lib": "/opt/unit/modules/python3.12.unit.so"
            },
            {
                "version": "3.11.0",
                "lib": "/opt/unit/modules/python3.11.unit.so"
            }
        ],
        "wasm": [ ... ]
}
lcrilly commented 46 minutes ago

I actually meant something different but then realised that it's not possible to have ambiguous module names.

So @ac000 's original example looks ideal.