This is an automated email from the ASF dual-hosted git repository.

tvb pushed a commit to branch tristan/remote-cli-options
in repository https://gitbox.apache.org/repos/asf/buildstream.git

commit b43b162e7cc132252ec71b50eea26ac9f16f9098
Author: Tristan van Berkom <[email protected]>
AuthorDate: Fri Feb 5 16:01:27 2021 +0900

    _remotespec.py: Adding RemoteSpec.new_from_string()
    
    A class method for use by the frontend to instantiate RemoteSpec objects
    from the command line.
---
 src/buildstream/_remotespec.py | 128 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 125 insertions(+), 3 deletions(-)

diff --git a/src/buildstream/_remotespec.py b/src/buildstream/_remotespec.py
index e91fb55..a37698e 100644
--- a/src/buildstream/_remotespec.py
+++ b/src/buildstream/_remotespec.py
@@ -44,6 +44,20 @@ class RemoteType(FastEnum):
         return ""
 
 
+# RemoteSpecPurpose():
+#
+# What a RemoteSpec is going to be used for.
+#
+# This is currently only used to control the behavior
+# of RemoteSpec.new_from_string(), after that, a RemoteSpec
+# has a `push` attribute which is either True or False.
+#
+class RemoteSpecPurpose(FastEnum):
+    ALL = 0  # Pushing and pulling
+    PUSH = 1  # Only pushing
+    PULL = 2  # Only pulling
+
+
 # RemoteSpec():
 #
 # This data structure holds all of the details required to
@@ -252,6 +266,116 @@ class RemoteSpec:
             spec_node=spec_node,
         )
 
+    # new_from_string():
+    #
+    # Creates a RemoteSpec() from a string, used to parse CLI parameters
+    #
+    # If certificates are passed, they are interpreted as relative to the
+    # current working directory.
+    #
+    # Args:
+    #    string: The user provided string
+    #    purpose: The purpose this RemoteSpec is intended for 
(RemoteSpecPurpose)
+    #
+    # Returns:
+    #    The described RemoteSpec instance.
+    #
+    # Raises:
+    #    RemoteError: In case parsing the string fails
+    #
+    @classmethod
+    def new_from_string(cls, string: str, purpose: int = 
RemoteSpecPurpose.ALL) -> "RemoteSpec":
+        url: Optional[str] = None
+        instance_name: Optional[str] = None
+        remote_type: str = RemoteType.ALL
+        push: bool = True
+        server_cert: Optional[str] = None
+        client_key: Optional[str] = None
+        client_cert: Optional[str] = None
+
+        if purpose == RemoteSpecPurpose.PULL:
+            push = False
+
+        split = string.split(",")
+        if len(split) > 1:
+            for split_string in split:
+                subsplit = split_string.split("=")
+
+                if len(subsplit) != 2:
+                    raise RemoteError(
+                        "Invalid format '{}' found in remote specification: 
{}".format(split_string, string)
+                    )
+
+                key: str = subsplit[0]
+                val: str = subsplit[1]
+
+                if key == "url":
+                    url = val
+                elif key == "instance-name":
+                    instance_name = val
+                elif key == "type":
+                    remote_type = val
+                    if remote_type not in [RemoteType.INDEX, 
RemoteType.STORAGE, RemoteType.ALL]:
+                        raise RemoteError(
+                            "Value for remote 'type' must be one of: 
{}".format(
+                                ", ".join([RemoteType.INDEX, 
RemoteType.STORAGE, RemoteType.ALL])
+                            )
+                        )
+                elif key == "push":
+
+                    # Provide a sensible error for `bst artifact push --remote 
url=http://pony.com,push=False ...`
+                    if purpose != RemoteSpecPurpose.ALL:
+                        raise RemoteError("The 'push' key is invalid and 
assumed to be {}".format(push))
+
+                    if val in ("True", "true"):
+                        push = True
+                    elif val in ("False", "false"):
+                        push = False
+                    else:
+                        raise RemoteError("Value for 'push' must be 'True' or 
'False'")
+                elif key == "server-cert":
+                    server_cert = cls._resolve_path(val, os.getcwd())
+                elif key == "client-key":
+                    client_key = cls._resolve_path(val, os.getcwd())
+                elif key == "client-cert":
+                    client_cert = cls._resolve_path(val, os.getcwd())
+                else:
+                    raise RemoteError("Unexpected key '{}' 
encountered".format(key))
+        else:
+            # No commas, only the URL was specified
+            url = string
+
+        if not url:
+            raise RemoteError("No URL specified in remote")
+
+        return cls(
+            remote_type,
+            url,
+            push=push,
+            server_cert=server_cert,
+            client_key=client_key,
+            client_cert=client_cert,
+            instance_name=instance_name,
+        )
+
+    # _resolve_path()
+    #
+    # Resolve a path relative to the base directory
+    #
+    # Args:
+    #    path: The path
+    #    basedir: The base directory
+    #
+    # Returns:
+    #    The resolved path
+    #
+    @classmethod
+    def _resolve_path(cls, path: str, basedir: Optional[str]) -> str:
+        path = os.path.expanduser(path)
+        if basedir:
+            path = os.path.join(basedir, path)
+        return path
+
     # _parse_auth():
     #
     # Parse the "auth" data
@@ -276,9 +400,7 @@ class RemoteSpec:
         for key in auth_keys:
             value = auth_node.get_str(key, None)
             if value:
-                value = os.path.expanduser(value)
-                if basedir:
-                    value = os.path.join(basedir, value)
+                value = cls._resolve_path(value, basedir)
             auth_values[key] = value
 
         server_cert = auth_values["server-cert"]

Reply via email to