Example #1
0
 def test_spin(self):
     self.spinner.start()
     self.assertTrue(Spinner.instances)
     sleep(0.4)
     self.spinner.stop()
     self.assertRegex(self.capturedOutput.getvalue(),
                      r".*[|/-\\]\sloading.*")
     Spinner.clear_spinner()
     self.assertEqual(Spinner.instances, [])
Example #2
0
    def test_context(self):
        with Spinner.spin(message="hello"):
            response = 1 + 1
        self.assertEqual(response, 2)
        self.assertRegex(self.capturedOutput.getvalue(), r".*[|/-\\]\shello.*")

        try:
            with Spinner.spin():
                raise Exception
        except:
            self.assertEqual(Spinner.instances, [])
Example #3
0
class TestSpinner(unittest.TestCase):
    def setUp(self):
        self.spinner = Spinner()
        self.capturedOutput = io.StringIO()
        sys.stdout = self.capturedOutput

    def tearDown(self):
        sys.stdout = sys.__stdout__

    def test_default(self):
        spinner = Spinner()
        self.assertEqual(spinner.message, "loading ...")
        self.assertEqual(spinner.pattern, "|/-\\")
        self.assertEqual(spinner.speed, 0.1)
        self.assertEqual(spinner.no_progress, False)

    def test_nondefault(self):
        spinner = Spinner(pattern="1234",
                          message="hello..",
                          speed=0.01,
                          no_progress=True)
        self.assertEqual(spinner.message, "hello..")
        self.assertEqual(spinner.pattern, "1234")
        self.assertEqual(spinner.speed, 0.01)
        self.assertEqual(spinner.no_progress, True)

    def test_no_progress(self):
        with Spinner.spin(no_progress=True):
            print("hello")
        self.assertEqual(self.capturedOutput.getvalue(), "hello\n")

    def test_spin(self):
        self.spinner.start()
        self.assertTrue(Spinner.instances)
        sleep(0.4)
        self.spinner.stop()
        self.assertRegex(self.capturedOutput.getvalue(),
                         r".*[|/-\\]\sloading.*")
        Spinner.clear_spinner()
        self.assertEqual(Spinner.instances, [])

    def test_context(self):
        with Spinner.spin(message="hello"):
            response = 1 + 1
        self.assertEqual(response, 2)
        self.assertRegex(self.capturedOutput.getvalue(), r".*[|/-\\]\shello.*")

        try:
            with Spinner.spin():
                raise Exception
        except:
            self.assertEqual(Spinner.instances, [])
Example #4
0
    def get_object_data(self, file_type: str = "") -> Dict[str, Any]:
        """Read the s3 object.

        Read the s3 object file and if is yaml/json file_type, load the file into dict
        currently is only used for cloudformation.

        :param file_type: type of file to process, supported value: yaml/json
        :type file_type: str
        :return: processed dict of json or yaml
        :raises InvalidFileType: when the file_type is invalid
        :rtype: Dict[str, Any]
        """
        with Spinner.spin(message="Reading file from s3 ..."):
            s3_object = self.resource.Object(self.bucket_name,
                                             self.path_list[0])
            body = s3_object.get()["Body"].read()
            body_dict: Dict[str, Any] = {}
            fileloader = FileLoader(body=body)
            if file_type == "yaml":
                body_dict = fileloader.process_yaml_body()
            elif file_type == "json":
                body_dict = fileloader.process_json_body()
            else:
                raise InvalidFileType
        return body_dict
Example #5
0
    def get_vpc_id(self,
                   multi_select: bool = False,
                   header: str = None,
                   no_progress: bool = False) -> Union[str, list]:
        """Get user selected vpc id through fzf.

        :param multi_select: allow multiple value selection
        :type multi_select: bool, optional
        :param header: header to display in fzf header
        :type header: str, optional
        :param no_progress: don't display progress bar, useful for ls command
        :type no_progress: bool, optional
        :return: selected vpc id
        :rtype: Union[str, list]
        """
        fzf = Pyfzf()
        with Spinner.spin(message="Fetching VPCs ...",
                          no_progress=no_progress):
            paginator = self.client.get_paginator("describe_vpcs")
            for result in paginator.paginate():
                response_generator = self._name_tag_generator(
                    result.get("Vpcs", []))
                fzf.process_list(response_generator, "VpcId", "IsDefault",
                                 "CidrBlock", "Name")
        return fzf.execute_fzf(empty_allow=True,
                               multi_select=multi_select,
                               header=header)
Example #6
0
def wait_drift_result(cloudformation: Cloudformation, drift_id: str) -> None:
    """Wait for the drift detection result.

    Since aws doesn't provide wait condition, creating a custom waiter.

    :param cloudformation: Cloudformation instance
    :type cloudformation: Cloudformation
    :param drift_id: the id of the drift detection
    :type drift_id: str
    """
    delay, max_attempts = cloudformation._get_waiter_config()
    attempts: int = 0
    response = None
    with Spinner.spin(message="Wating for drift detection to complete ..."):
        while attempts <= max_attempts:
            time.sleep(delay)
            response = cloudformation.client.describe_stack_drift_detection_status(
                StackDriftDetectionId=drift_id)
            if response.get("DetectionStatus") != "DETECTION_IN_PROGRESS":
                break
    if response is not None:
        response.pop("ResponseMetadata", None)
        print(json.dumps(response, indent=4, default=str))
        print(80 * "-")
        if response["DetectionStatus"] == "DETECTION_COMPLETE":
            print("StackDriftStatus: %s" %
                  response.get("StackResourceDriftStatus"))
            print("DriftedStackResourceCount: %s" %
                  response.get("DriftedStackResourceCount"))
        else:
            print("Drift detection failed")
    else:
        print("Waiter failed: Max attempts exceeded")
Example #7
0
    def set_arns(
        self,
        arns: Optional[Union[str, list]] = None,
        empty_allow: bool = False,
        header: Optional[str] = None,
        multi_select: bool = False,
    ) -> None:
        """Set cloudwatch arns for further operations.

        :param arns: arns to init
        :type arns: Union[list, str], optional
        :param empty_allow: allow empty fzf selection
        :type empty_allow: bool, optional
        :param header: header in fzf
        :type header: str, optional
        :param multi_select: allow multi selection in fzf
        :type multi_select: bool, optional
        """
        if not arns:
            fzf = Pyfzf()
            with Spinner.spin(message="Fetching cloudwatch alarms ..."):
                paginator = self.client.get_paginator("describe_alarms")
                for result in paginator.paginate():
                    if result.get("CompositeAlarms"):
                        fzf.process_list(result["CompositeAlarms"], "AlarmArn")
                    if result.get("MetricAlarms"):
                        fzf.process_list(result["MetricAlarms"], "AlarmArn")
            arns = fzf.execute_fzf(
                empty_allow=empty_allow, multi_select=multi_select, header=header
            )
        if type(arns) == str:
            self.arns[0] = str(arns)
        elif type(arns) == list:
            self.arns = list(arns)
Example #8
0
    def set_arns(
        self,
        arns: Optional[Union[str, List[str]]] = None,
        empty_allow: bool = False,
        header: Optional[str] = None,
        multi_select: bool = False,
    ) -> None:
        """Set the sns arn for other operations.

        :param arns: arns to init
        :type arns: Union[list, str], optional
        :param empty_allow: allow empty fzf selection
        :type empty_allow: bool, optional
        :param header: header in fzf
        :type header: str, optional
        :param multi_select: allow multi selection in fzf
        :type multi_select: bool, optional
        """
        if not arns:
            fzf = Pyfzf()
            with Spinner.spin(message="Fetching sns topics ..."):
                paginator = self.client.get_paginator("list_topics")
                for result in paginator.paginate():
                    fzf.process_list(result.get("Topics", []), "TopicArn")
            arns = fzf.execute_fzf(empty_allow=empty_allow,
                                   multi_select=multi_select,
                                   header=header)
        if type(arns) == str:
            self.arns[0] = str(arns)
        elif type(arns) == list:
            self.arns = list(arns)
Example #9
0
    def wait(self, waiter_name: str, message: str = None) -> None:
        """Wait for the operation to be completed.

        This method uses the boto3 waiter.

        :param waiter_name: name of boto3 waiter
        :type waiter_name: str
        :param message: message to display during loading
        :type message: str, optional
        """
        with Spinner.spin(message=message):
            waiter = self.client.get_waiter(waiter_name)
            waiter_config = os.getenv("FZFAWS_EC2_WAITER",
                                      os.getenv("FZFAWS_GLOBAL_WAITER", ""))
            delay: int = 15
            max_attempts: int = 40
            if waiter_config:
                waiter_config = json.loads(waiter_config)
                delay = int(waiter_config.get("delay", 15))
                max_attempts = int(waiter_config.get("max_attempts", 40))
            waiter.wait(
                InstanceIds=self.instance_ids,
                WaiterConfig={
                    "Delay": delay,
                    "MaxAttempts": max_attempts
                },
            )
Example #10
0
    def get_stack_resources(
        self, empty_allow: bool = False, header: str = None, no_progress: bool = False
    ) -> List[str]:
        """List all stack logical resources and return the selected resources.

        :param empty_allow: allow empty selection
        :type empty_allow: bool, optional
        :param header: information to be displayed in fzf header
        :type header: str, optional
        :param no_progress: don't display progress bar, useful for ls command
        :type no_progress: bool, optional
        :return: selected list of logical resources LogicalResourceId
        :rtype: List[str]
        """
        fzf = Pyfzf()
        with Spinner.spin(
            message="Fetching stack resources ...", no_progress=no_progress
        ):
            paginator = self.client.get_paginator("list_stack_resources")
            for result in paginator.paginate(StackName=self.stack_name):
                for resource in result.get("StackResourceSummaries"):
                    resource["Drift"] = resource.get("DriftInformation").get(
                        "StackResourceDriftStatus"
                    )
                fzf.process_list(
                    result.get("StackResourceSummaries"),
                    "LogicalResourceId",
                    "ResourceType",
                    "Drift",
                )
        return list(
            fzf.execute_fzf(multi_select=True, header=header, empty_allow=empty_allow)
        )
Example #11
0
    def get_security_groups(
        self,
        multi_select: bool = False,
        return_attr: str = "id",
        header: str = None,
        no_progress: bool = False,
    ) -> Union[str, list]:
        """Use paginator to get the user selected security groups.

        :param multi_select: allow multiple value selection
        :type multi_select: bool, optional
        :param return_attr: what attribute to return (id|name)
        :type return_attr: str, optional
        :param header: header to display in fzf
        :type header: str, optional
        :param no_progress: don't display progress bar, useful for ls command
        :type no_progress: bool, optional
        :return: selected security groups/ids
        :rtype: Union[str, list]
        """
        fzf = Pyfzf()
        with Spinner.spin(message="Fetching SecurityGroups ...",
                          no_progress=no_progress):
            paginator = self.client.get_paginator("describe_security_groups")
            for result in paginator.paginate():
                response_generator = self._name_tag_generator(
                    result.get("SecurityGroups", []))
                if return_attr == "id":
                    fzf.process_list(response_generator, "GroupId",
                                     "GroupName", "Name")
                elif return_attr == "name":
                    fzf.process_list(response_generator, "GroupName", "Name")
        return fzf.execute_fzf(multi_select=multi_select,
                               empty_allow=True,
                               header=header)
Example #12
0
 def test_nondefault(self):
     spinner = Spinner(pattern="1234",
                       message="hello..",
                       speed=0.01,
                       no_progress=True)
     self.assertEqual(spinner.message, "hello..")
     self.assertEqual(spinner.pattern, "1234")
     self.assertEqual(spinner.speed, 0.01)
     self.assertEqual(spinner.no_progress, True)
Example #13
0
    def _get_selected_param_value(self, type_name: str,
                                  param_header: str) -> str:
        """Use fzf to display aws specific parameters.

        :param type_name: name of the parameter type
        :type type_name: str
        :param param_header: information about current parameter
        :type param_header: str
        :return: return the selected value
        :rtype: str
        """
        fzf = Pyfzf()

        if type_name == "AWS::EC2::KeyPair::KeyName":
            with Spinner.spin(message="Fetching KeyPair ..."):
                response = self.ec2.client.describe_key_pairs()
                response_list = response["KeyPairs"]
            fzf.process_list(response_list, "KeyName", empty_allow=True)
        elif type_name == "AWS::EC2::SecurityGroup::Id":
            return str(self.ec2.get_security_groups(header=param_header))
        elif type_name == "AWS::EC2::AvailabilityZone::Name":
            with Spinner.spin(message="Fetching AvailabilityZones ..."):
                response = self.ec2.client.describe_availability_zones()
                response_list = response.get("AvailabilityZones", [])
            fzf.process_list(response_list, "ZoneName", empty_allow=True)
        elif type_name == "AWS::EC2::Instance::Id":
            return str(self.ec2.get_instance_id(header=param_header))
        elif type_name == "AWS::EC2::SecurityGroup::GroupName":
            return str(
                self.ec2.get_security_groups(return_attr="name",
                                             header=param_header))
        elif type_name == "AWS::EC2::Subnet::Id":
            return str(self.ec2.get_subnet_id(header=param_header))
        elif type_name == "AWS::EC2::Volume::Id":
            return str(self.ec2.get_volume_id(header=param_header))
        elif type_name == "AWS::EC2::VPC::Id":
            return str(self.ec2.get_vpc_id(header=param_header))
        elif type_name == "AWS::Route53::HostedZone::Id":
            self.route53.set_zone_id()
            return self.route53.zone_ids[0]
        return str(fzf.execute_fzf(empty_allow=True, header=param_header))
Example #14
0
    def set_ec2_instance(self,
                         multi_select: bool = True,
                         header: str = None,
                         no_progress: bool = False) -> None:
        """Set ec2 instance for current operation.

        :param multi_select: enable multi select
        :type multi_select: bool, optional
        :param header: helper information to display in fzf header
        :type header: str, optional
        :param no_progress: don't display progress bar, useful for ls command
        :type no_progress: bool, optional
        """
        fzf = Pyfzf()
        with Spinner.spin(message="Fetching EC2 instances ...",
                          no_progress=no_progress):
            paginator = self.client.get_paginator("describe_instances")
            for result in paginator.paginate():
                response_generator = self._instance_generator(
                    result["Reservations"])
                fzf.process_list(
                    response_generator,
                    "InstanceId",
                    "Status",
                    "InstanceType",
                    "Name",
                    "KeyName",
                    "PublicDnsName",
                    "PublicIpAddress",
                    "PrivateIpAddress",
                )
        selected_instance = fzf.execute_fzf(multi_select=multi_select,
                                            header=header,
                                            print_col=0)

        if multi_select:
            self.instance_ids[:] = []
            self.instance_list[:] = []
            for instance in selected_instance:
                curr = fzf.format_selected_to_dict(str(instance))
                self.instance_list.append(curr)
                self.instance_ids.append(curr["InstanceId"])
        else:
            self.instance_ids[:] = []
            self.instance_list[:] = []
            self.instance_list.append(
                fzf.format_selected_to_dict(str(selected_instance)))
            self.instance_ids.append(self.instance_list[0]["InstanceId"])
        if len(self.instance_ids) == 0:
            self.instance_ids = [""]
        if len(self.instance_list) == 0:
            self.instance_list = [{}]
Example #15
0
    def set_s3_bucket(self, header: str = "", no_progress: bool = False) -> None:
        """List bucket through fzf and let user select a bucket.

        :param header: header to display in fzf header
        :type header: str, optional
        :param no_progress: don't display progress bar, useful for ls command
        :type no_progress: bool, optional
        """
        fzf = Pyfzf()
        with Spinner.spin(message="Fetching s3 buckets ...", no_progress=no_progress):
            response = self.client.list_buckets()
        fzf.process_list(response["Buckets"], "Name")
        self.bucket_name = str(fzf.execute_fzf(header=header))
Example #16
0
    def _get_list_param_value(self, type_name: str,
                              param_header: str) -> List[str]:
        """Handle operation if parameter type is a list type.

        This function is almost the same as _get_selected_param_value besides its
        handling list type rather than single vaiable type.

        :param type_name: name of the type of the parameter
        :type type_name: str
        :param param_header: information about the current parameter
        :type param_header: str
        :return: processed list of selection from the user
        :rtype: List[str]
        """
        fzf = Pyfzf()

        if type_name == "List<AWS::EC2::AvailabilityZone::Name>":
            with Spinner.spin(message="Fetching AvailabilityZones ..."):
                response = self.ec2.client.describe_availability_zones()
                response_list = response["AvailabilityZones"]
            fzf.process_list(response_list, "ZoneName", empty_allow=True)
        elif type_name == "List<AWS::EC2::Instance::Id>":
            return list(
                self.ec2.get_instance_id(multi_select=True,
                                         header=param_header))
        elif type_name == "List<AWS::EC2::SecurityGroup::GroupName>":
            return list(
                self.ec2.get_security_groups(multi_select=True,
                                             return_attr="name",
                                             header=param_header))
        elif type_name == "List<AWS::EC2::SecurityGroup::Id>":
            return list(
                self.ec2.get_security_groups(multi_select=True,
                                             header=param_header))
        elif type_name == "List<AWS::EC2::Subnet::Id>":
            return list(
                self.ec2.get_subnet_id(multi_select=True, header=param_header))
        elif type_name == "List<AWS::EC2::Volume::Id>":
            return list(
                self.ec2.get_volume_id(multi_select=True, header=param_header))
        elif type_name == "List<AWS::EC2::VPC::Id>":
            return list(
                self.ec2.get_vpc_id(multi_select=True, header=param_header))
        elif type_name == "List<AWS::Route53::HostedZone::Id>":
            self.route53.set_zone_id(multi_select=True)
            return self.route53.zone_ids
        return list(
            fzf.execute_fzf(multi_select=True,
                            empty_allow=True,
                            header=param_header))
Example #17
0
    def wait(self, waiter_name: str, message: str = None, **kwargs) -> None:
        """Wait for the operation to be completed.

        Require a waiter_name approved by boto3, because it's using boto3 waiter.

        :param waiter_name: name for the boto3 waiter
        :type waiter_name: str
        :param message: message to display for spinner
        :type message: str, optional
        """
        with Spinner.spin(message=message):
            waiter = self.client.get_waiter(waiter_name)
            delay, max_attempts = self._get_waiter_config()
            waiter.wait(
                StackName=self.stack_name,
                WaiterConfig={"Delay": delay, "MaxAttempts": max_attempts},
                **kwargs
            )
Example #18
0
    def set_stack(self, no_progress=False) -> None:
        """Store the selected stack into the instance attribute.

        :param no_progress: don't display progress bar, useful for ls command
        :type no_progress: bool, optional
        """
        fzf = Pyfzf()
        with Spinner.spin(
            message="Fetching cloudformation stacks ...", no_progress=no_progress
        ):
            paginator = self.client.get_paginator("describe_stacks")
            response = paginator.paginate()
            stack_generator = self._get_stack_generator(response)
            for result in response:
                fzf.process_list(
                    result["Stacks"], "StackName", "StackStatus", "Description"
                )
        self.stack_name = str(fzf.execute_fzf(empty_allow=False))
        self.stack_details = search_dict_in_list(
            self.stack_name, stack_generator, "StackName"
        )
Example #19
0
    def get_instance_id(self,
                        multi_select: bool = False,
                        header: str = None) -> Union[str, list]:
        """Use paginator to get instance id and return it.

        :param multi_select: allow multiple value selection
        :type multi_select: bool, optional
        :param header: header to display in fzf header
        :type header: str, optional
        :return: selected instance id
        :rtype: Union[str, list]
        """
        fzf = Pyfzf()
        with Spinner.spin(message="Fetching EC2 instances ..."):
            paginator = self.client.get_paginator("describe_instances")
            for result in paginator.paginate():
                response_generator = self._instance_id_generator(
                    result.get("Reservations", []))
                fzf.process_list(response_generator, "InstanceId", "Name")
        return fzf.execute_fzf(multi_select=multi_select,
                               empty_allow=True,
                               header=header)
Example #20
0
    def set_keyids(
        self,
        keyids: Optional[Union[list, str]] = None,
        header: Optional[str] = None,
        multi_select: bool = False,
        empty_allow: bool = True,
    ) -> None:
        """Set the key for kms for further operations.

        :param keyids: keyids to set
        :type keyids: Union[list, str], optional
        :param header: header information in fzf
        :type header: str, optional
        :param multi_select: enable multi select
        :type multi_select: bool, optional
        :param empty_allow: allow empty selection
        :type empty_allow: bool, optional
        """
        if not keyids:
            fzf = Pyfzf()
            with Spinner.spin(message="Fetching kms keys ..."):
                paginator = self.client.get_paginator("list_aliases")
                for result in paginator.paginate():
                    aliases = [
                        alias for alias in result.get("Aliases")
                        if alias.get("TargetKeyId")
                    ]
                    fzf.process_list(aliases, "TargetKeyId", "AliasName",
                                     "AliasArn")
            keyids = fzf.execute_fzf(header=header,
                                     multi_select=multi_select,
                                     empty_allow=empty_allow)
        if type(keyids) == str:
            self.keyids[0] = str(keyids)
        elif type(keyids) == list:
            self.keyids = list(keyids)
Example #21
0
    def set_zone_id(
        self,
        zone_ids: Optional[Union[str, List[str]]] = None,
        multi_select: bool = False,
    ) -> None:
        """Set the hostedzone id for futher operations.

        :param zone_ids: list of zone_ids to set
        :type zone_ids: list, optional
        :param multi_select: allow multi_select
        :type multi_select: bool, optional
        """
        if zone_ids is None:
            fzf = Pyfzf()
            with Spinner.spin(message="Fetching hostedzones ..."):
                paginator = self.client.get_paginator("list_hosted_zones")
                for result in paginator.paginate():
                    result = self._process_hosted_zone(result["HostedZones"])
                    fzf.process_list(result, "Id", "Name")
            zone_ids = fzf.execute_fzf(multi_select=multi_select, empty_allow=True)
        if type(zone_ids) == str:
            self.zone_ids[0] = str(zone_ids)
        elif type(zone_ids) == list:
            self.zone_ids = list(zone_ids)
Example #22
0
 def test_no_progress(self):
     with Spinner.spin(no_progress=True):
         print("hello")
     self.assertEqual(self.capturedOutput.getvalue(), "hello\n")
Example #23
0
 def setUp(self):
     self.spinner = Spinner()
     self.capturedOutput = io.StringIO()
     sys.stdout = self.capturedOutput
Example #24
0
    def get_object_version(
        self,
        bucket: str = "",
        key: str = "",
        delete: bool = False,
        select_all: bool = False,
        non_current: bool = False,
        multi_select: bool = True,
        no_progress: bool = False,
    ) -> List[Dict[str, str]]:
        """List object versions through fzf.
        
        :param bucket: object's bucketname, if not set, class instance's bucket_name will be used
        :type bucket: str, optional
        :param key: object's key, if not set, class instance's path_list[0] will be used
        :type key: str, optional
        :param delete: allow to choose delete marker
        :type delete: bool, optional
        :param select_all: skip fzf and select all version and put into return list
        :type select_all: bool, optional
        :param non_current: only put non_current versions into list
        :type non_current: bool, optional
        :param multi_select: allow multi selection
        :type multi_select: bool, optional
        :param no_progress: don't display progress bar, useful for ls command
        :type no_progress: bool, optional
        :return: list of selected versions
        :rtype: List[Dict[str, str]]

        Example return value:
            [{'Key': s3keypath, 'VersionId': s3objectid}]
        """
        bucket = bucket if bucket else self.bucket_name
        key_list: list = []
        fzf = Pyfzf()

        if key:
            key_list.append(key)
        else:
            key_list.extend(self.path_list)
        selected_versions: list = []
        for key in key_list:
            response_generator: Union[list, Generator[Dict[str, str], None,
                                                      None]] = []
            with Spinner.spin(message="Fetching object versions ...",
                              no_progress=no_progress):
                paginator = self.client.get_paginator("list_object_versions")
                for result in paginator.paginate(Bucket=bucket, Prefix=key):
                    response_generator = self._version_generator(
                        result.get("Versions", []),
                        result.get("DeleteMarkers", []),
                        non_current,
                        delete,
                    )
                    if not select_all:
                        fzf.process_list(
                            response_generator,
                            "VersionId",
                            "Key",
                            "IsLatest",
                            "DeleteMarker",
                            "LastModified",
                        )
                    else:
                        selected_versions.extend([{
                            "Key":
                            key,
                            "VersionId":
                            version.get("VersionId")
                        } for version in response_generator])

            if not select_all:
                if delete and multi_select:
                    for result in fzf.execute_fzf(multi_select=True):
                        selected_versions.append({
                            "Key": key,
                            "VersionId": result
                        })
                else:
                    selected_versions.append({
                        "Key":
                        key,
                        "VersionId":
                        str(fzf.execute_fzf())
                    })
        return selected_versions
Example #25
0
    def set_s3_path(self, download: bool = False) -> None:
        """Set 'path' of s3 to upload or download.

        s3 folders are not actually folder, found this path listing on
        https://github.com/boto/boto3/issues/134#issuecomment-116766812

        This method would set the 'path' for s3 however the self.path_list cannot be used
        as the destination of upload immediately. This only set the path
        without handling different upload sceanario. Please use the
        get_s3_destination_key after set_s3_path to obtain the correct destination key

        :param download: if not download, add append option
        :type download: bool, optional
        :raises NoSelectionMade: when user did not make a bucket selection, exit
        """
        selected_option = self._get_path_option(download=download)

        if selected_option == "input":
            self.path_list[0] = input("Input the path(newname or newpath/): ")
        elif selected_option == "root":
            # print("S3 file path is set to root")
            pass
        elif selected_option == "append" or selected_option == "interactively":
            paginator = self.client.get_paginator("list_objects")
            fzf = Pyfzf()
            parents = []
            # interactively search down 'folders' in s3
            while True:
                if len(parents) > 0:
                    fzf.append_fzf("\033[34m../\033[0m\n")
                fzf.append_fzf("\033[33m./\033[0m\n")
                with Spinner.spin(message="Fetching s3 objects ..."):
                    preview: str = ""
                    for result in paginator.paginate(
                            Bucket=self.bucket_name,
                            Prefix=self.path_list[0],
                            Delimiter="/",
                    ):
                        for prefix in result.get("CommonPrefixes", []):
                            fzf.append_fzf("%s\n" % prefix.get("Prefix"))
                        for content in result.get("Contents", []):
                            preview += content.get("Key")
                            preview += "^"

                # has to use tr to transform the string to new line during preview by fzf
                # not sure why, but if directly use \n, fzf preview interpret as a new command
                # TODO: findout why
                selected_path = str(
                    fzf.execute_fzf(
                        print_col=0,
                        header=
                        'PWD: s3://%s/%s (select "./" will the current path)' %
                        (self.bucket_name, self.path_list[0]),
                        preview="echo %s | tr '^' '\n'" % preview.rstrip(),
                    ))
                if not selected_path:
                    raise NoSelectionMade
                if selected_path == "../":
                    self.path_list[0] = parents.pop()
                elif selected_path == "./":
                    break
                else:
                    parents.append(self.path_list[0])
                    self.path_list[0] = selected_path
                # reset fzf string
                fzf.fzf_string = ""

            if selected_option == "append":
                print("Current PWD is s3://%s/%s" %
                      (self.bucket_name, self.path_list[0]))
                new_path = input(
                    "Input the new path to append(newname or newpath/): ")
                self.path_list[0] += new_path
        print("S3 file path is set to %s" %
              (self.path_list[0] if self.path_list[0] else "root"))
Example #26
0
    def set_s3_object(
        self,
        version: bool = False,
        multi_select: bool = False,
        deletemark: bool = False,
        no_progress: bool = False,
    ) -> None:
        """List object within a bucket and let user select a object.

        Stores the file path and the filetype into the instance attributes
        using paginator to get all results.

        All of the deleted object are displayed in red color when version mode
        is enabled.

        :param version: enable version search
        :type version: bool, optional
        :param multi_select: enable multi selection
        :type multi_select: bool, optional
        :param deletemark: show deletemark object in the list
        :type deletemark: bool, optional
        :param no_progress: don't display progress bar, useful for ls command
        :type no_progress: bool, optional
        :raises NoSelectionMade: when there is no selection made
        """
        fzf = Pyfzf()

        if not version:
            paginator = self.client.get_paginator("list_objects")
            with Spinner.spin(message="Fetching s3 objects ...",
                              no_progress=no_progress):
                for result in paginator.paginate(Bucket=self.bucket_name):
                    for file in result.get("Contents", []):
                        if file.get("Key").endswith(
                                "/") or not file.get("Key"):
                            # user created dir in S3 console will appear in the result and is not operatable
                            continue
                        fzf.append_fzf("Key: %s\n" % file.get("Key"))
            if multi_select:
                self.path_list = list(
                    fzf.execute_fzf(multi_select=True, delimiter=": "))
            else:
                self.path_list[0] = str(fzf.execute_fzf(delimiter=": "))

        else:
            paginator = self.client.get_paginator("list_object_versions")
            with Spinner.spin(message="Fetching s3 objects ...",
                              no_progress=no_progress):
                results = paginator.paginate(Bucket=self.bucket_name)
                version_obj_genrator = self._uniq_object_generator(
                    results, deletemark)
                generated = False
                for item in version_obj_genrator:
                    generated = True
                    fzf.append_fzf(item + "\n")
                if not generated:
                    raise NoSelectionMade
            if multi_select:
                self.path_list = list(
                    fzf.execute_fzf(delimiter=": ", multi_select=True))
            else:
                self.path_list[0] = str(fzf.execute_fzf(delimiter=": "))
Example #27
0
 def test_default(self):
     spinner = Spinner()
     self.assertEqual(spinner.message, "loading ...")
     self.assertEqual(spinner.pattern, "|/-\\")
     self.assertEqual(spinner.speed, 0.1)
     self.assertEqual(spinner.no_progress, False)