Repository: Azure/azure-functions-python-worker

Issue:
Update python language worker to support handling new command line arguments with functions- prefix
The command-line arguments that the language worker receives from the Functions Host have generic names (such as 'host' and 'port'). This can cause conflicts with other environment variables in certain environments. To address this issue, the Functions Host will now send a new set of arguments prefixed with `functions-<argumentname>`. This will be in addition to the existing arguments.

With the new planned host change, host code will send command line arguments like below. 

> -host 127.0.0.1 --port 50821 --workerId e9efd817-47a1-45dc-9e20-e6f975d7a025 --requestId cbef5957-cdb3-4462-9ee7-ac9f91be0a51 --grpcMaxMessageLength 2147483647 --functions-uri http://127.0.0.1:50821 --functions-workerid e9efd817-47a1-45dc-9e20-e6f975d7a025 --functions-requestid cbef5957-cdb3-4462-9ee7-ac9f91be0a51 --functions-grpcmaxmessagelength 2147483647

The new arguments sent are: 

1. functions-uri
2. functions-request-id
3. functions-worker-id
4. functions-grpc-max-message-length

Initial sanity testing showed that python worker fails to start when the new arguments are sent. We need to fix the python worker to handle these new arguments.

#### Expected behavior with the fix:

1. Use the new arguments (the one with "functions-" prefix).
2. If the new arguments are not present, use the old ones (PORT, HOST etc..)

See https://github.com/Azure/azure-functions-host/issues/9504 for more details.




Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/Azure/azure-functions-python-worker/pull/1318?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure) Report
> Merging [#1318](https://app.codecov.io/gh/Azure/azure-functions-python-worker/pull/1318?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure) (a306a7b) into [dev](https://app.codecov.io/gh/Azure/azure-functions-python-worker/commit/d0135b091e8df03f8ac131894e0ec66f780de049?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure) (d0135b0) will **decrease** coverage by `52.62%`.
> The diff coverage is `0.00%`.

> :exclamation: Current head a306a7b differs from pull request most recent head b8e579a. Consider uploading reports for the commit b8e579a to get more accurate results

```diff
@@             Coverage Diff             @@
##              dev    #1318       +/-   ##
===========================================
- Coverage   84.70%   32.08%   -52.62%     
===========================================
  Files          34       34               
  Lines        1922     1926        +4     
  Branches      366      365        -1     
===========================================
- Hits         1628      618     -1010     
- Misses        224     1307     +1083     
+ Partials       70        1       -69     
```

| [Flag](https://app.codecov.io/gh/Azure/azure-functions-python-worker/pull/1318/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure) | Coverage Δ | |
|---|---|---|
| [unittests](https://app.codecov.io/gh/Azure/azure-functions-python-worker/pull/1318/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure) | `32.08% <0.00%> (-52.57%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Files Changed](https://app.codecov.io/gh/Azure/azure-functions-python-worker/pull/1318?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure) | Coverage Δ | |
|---|---|---|
| [azure\_functions\_worker/main.py](https://app.codecov.io/gh/Azure/azure-functions-python-worker/pull/1318?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure#diff-YXp1cmVfZnVuY3Rpb25zX3dvcmtlci9tYWluLnB5) | `0.00% <0.00%> (ø)` | |

... and [23 files with indirect coverage changes](https://app.codecov.io/gh/Azure/azure-functions-python-worker/pull/1318/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure)



Patch:
diff --git a/azure_functions_worker/main.py b/azure_functions_worker/main.py
index 3edf284..4e1f70a 100644
--- a/azure_functions_worker/main.py
+++ b/azure_functions_worker/main.py
@@ -2,7 +2,6 @@
 # Licensed under the MIT License.
 """Main entrypoint."""
 
-
 import argparse
 
 
@@ -26,6 +25,18 @@ def parse_args():
                              'syslog, or a file path')
     parser.add_argument('--grpcMaxMessageLength', type=int,
                         dest='grpc_max_msg_len')
+    parser.add_argument('--functions-uri', dest='functions_uri', type=str,
+                        help='URI with IP Address and Port used to'
+                             ' connect to the Host via gRPC.')
+    parser.add_argument('--functions-request-id', dest='functions_request_id',
+                        type=str, help='Request ID used for gRPC communication '
+                                       'with the Host.')
+    parser.add_argument('--functions-worker-id',
+                        dest='functions_worker_id', type=str,
+                        help='Worker ID assigned to this language worker.')
+    parser.add_argument('--functions-grpc-max-message-length', type=int,
+                        dest='functions_grpc_max_msg_len',
+                        help='Max grpc message length for Functions')
     return parser.parse_args()
 
 


Test patch:
diff --git a/tests/unittests/test_main.py b/tests/unittests/test_main.py
new file mode 100644
index 0000000..2fe29c8
--- /dev/null
+++ b/tests/unittests/test_main.py
@@ -0,0 +1,79 @@
+import unittest
+import sys
+from unittest.mock import patch
+from azure_functions_worker.main import parse_args
+
+
+class TestMain(unittest.TestCase):
+
+    @patch.object(sys, 'argv',
+                  ['xxx', '--host', '127.0.0.1',
+                   '--port', '50821',
+                   '--workerId', 'e9efd817-47a1-45dc-9e20-e6f975d7a025',
+                   '--requestId', 'cbef5957-cdb3-4462-9ee7-ac9f91be0a51',
+                   '--grpcMaxMessageLength', '2147483647',
+                   '--functions-uri', 'http://127.0.0.1:50821',
+                   '--functions-worker-id',
+                   'e9efd817-47a1-45dc-9e20-e6f975d7a025',
+                   '--functions-request-id',
+                   'cbef5957-cdb3-4462-9ee7-ac9f91be0a51',
+                   '--functions-grpc-max-message-length', '2147483647'])
+    def test_all_args(self):
+        args = parse_args()
+        self.assertEqual(args.host, '127.0.0.1')
+        self.assertEqual(args.port, 50821)
+        self.assertEqual(args.worker_id,
+                         'e9efd817-47a1-45dc-9e20-e6f975d7a025')
+        self.assertEqual(args.request_id,
+                         'cbef5957-cdb3-4462-9ee7-ac9f91be0a51')
+        self.assertEqual(args.grpc_max_msg_len, 2147483647)
+        self.assertEqual(args.functions_uri, 'http://127.0.0.1:50821')
+        self.assertEqual(args.functions_worker_id,
+                         'e9efd817-47a1-45dc-9e20-e6f975d7a025')
+        self.assertEqual(args.functions_request_id,
+                         'cbef5957-cdb3-4462-9ee7-ac9f91be0a51')
+        self.assertEqual(args.functions_grpc_max_msg_len, 2147483647)
+
+    @patch.object(sys, 'argv',
+                  ['xxx', '--host', '127.0.0.1',
+                   '--port', '50821',
+                   '--workerId', 'e9efd817-47a1-45dc-9e20-e6f975d7a025',
+                   '--requestId', 'cbef5957-cdb3-4462-9ee7-ac9f91be0a51',
+                   '--grpcMaxMessageLength', '2147483647'])
+    def test_old_args(self):
+        args = parse_args()
+        self.assertEqual(args.host, '127.0.0.1')
+        self.assertEqual(args.port, 50821)
+        self.assertEqual(args.worker_id,
+                         'e9efd817-47a1-45dc-9e20-e6f975d7a025')
+        self.assertEqual(args.request_id,
+                         'cbef5957-cdb3-4462-9ee7-ac9f91be0a51')
+        self.assertEqual(args.grpc_max_msg_len, 2147483647)
+        self.assertIsNone(args.functions_uri)
+        self.assertIsNone(args.functions_worker_id)
+        self.assertIsNone(args.functions_request_id)
+        self.assertIsNone(args.functions_grpc_max_msg_len)
+
+    @patch.object(sys, 'argv',
+                  ['xxx', '--functions-uri', 'http://127.0.0.1:50821',
+                   '--functions-worker-id',
+                   'e9efd817-47a1-45dc-9e20-e6f975d7a025',
+                   '--functions-request-id',
+                   'cbef5957-cdb3-4462-9ee7-ac9f91be0a51',
+                   '--functions-grpc-max-message-length', '2147483647'])
+    def test_new_args(self):
+        args = parse_args()
+        self.assertEqual(args.functions_uri, 'http://127.0.0.1:50821')
+        self.assertEqual(args.functions_worker_id,
+                         'e9efd817-47a1-45dc-9e20-e6f975d7a025')
+        self.assertEqual(args.functions_request_id,
+                         'cbef5957-cdb3-4462-9ee7-ac9f91be0a51')
+        self.assertEqual(args.functions_grpc_max_msg_len, 2147483647)
+
+    @patch.object(sys, 'argv', ['xxx', '--host', 'dummy_host',
+                                '--port', '12345',
+                                '--invalid-arg', 'invalid_value'])
+    def test_invalid_args(self):
+        with self.assertRaises(SystemExit) as context:
+            parse_args()
+        self.assertEqual(context.exception.code, 2)