Exemplo n.º 1
0
    def _set_policy(self,
                    update: bool = False,
                    search_from_root: bool = False) -> None:
        """Set the stack_policy for the stack.

        Used to prevent update on certain resources.

        :param update: determine if stack is updating, if true, set different args
            aws cloudformation takes StackPolicyBody for creation and StackPolicyDuringUpdateBody for update overwrite
        :type update: bool, optional
        :param search_from_root: search files from root
        :type search_from_root: bool, optional
        """
        print(80 * "-")
        fzf = Pyfzf()
        file_path: str = str(
            fzf.get_local_file(
                search_from_root=search_from_root,
                cloudformation=True,
                empty_allow=True,
                header="select the policy document you would like to use",
            ))
        if not update and file_path:
            with open(str(file_path), "r") as body:
                body = body.read()
                self._extra_args["StackPolicyBody"] = body
        elif update and file_path:
            with open(str(file_path), "r") as body:
                body = body.read()
                self._extra_args["StackPolicyDuringUpdateBody"] = body
Exemplo n.º 2
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)
Exemplo n.º 3
0
    def test_constructor(self, mocked_sys):
        self.assertRegex(
            self.fzf.fzf_path,
            r".*/fzfaws.*/libs/fzf-[0-9]\.[0-9]+\.[0-9]-(linux|darwin)_(386|amd64)",
        )
        self.assertEqual("", self.fzf.fzf_string)

        mocked_sys.maxsize = 4294967295
        mocked_sys.platform = "linux"
        fzf = Pyfzf()
        self.assertRegex(
            fzf.fzf_path,
            r".*/fzfaws.*/libs/fzf-[0-9]\.[0-9]+\.[0-9]-linux_386")

        mocked_sys.maxsize = 42949672951
        mocked_sys.platform = "darwin"
        fzf = Pyfzf()
        self.assertRegex(
            fzf.fzf_path,
            r".*/fzfaws.*/libs/fzf-[0-9]\.[0-9]+\.[0-9]-darwin_amd64")

        mocked_sys.maxsize = 42949672951
        mocked_sys.platform = "windows"
        mocked_sys.exit.side_effect = sys.exit
        self.assertRaises(SystemExit, Pyfzf)
        self.assertEqual(
            self.capturedOutput.getvalue(),
            "fzfaws currently is only compatible with python3.6+ on MacOS or Linux\n",
        )
Exemplo n.º 4
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)
        )
Exemplo n.º 5
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)
Exemplo n.º 6
0
def create_stack(
    profile: Union[str, bool] = False,
    region: Union[str, bool] = False,
    local_path: Union[str, bool] = False,
    root: bool = False,
    wait: bool = False,
    extra: bool = False,
    bucket: str = None,
    version: Union[str, bool] = False,
) -> None:
    """Handle the creation of the cloudformation stack.

    :param profile: use a different profile for this operation
    :type profile: Union[bool, str], optional
    :param region: use a different region for this operation
    :type region: Union[bool, str], optional
    :param local_path: Select a template from local machine
    :type local_path: Union[bool, str], optional
    :param root: Search local file from root directory
    :type root: bool, optional
    :param wait: wait for stack to be completed before exiting the program
    :type wait: bool, optional
    :param extra: configure extra options for the stack, (tags, IAM, termination protection etc..)
    :type extra: bool, optional
    :param bucket: specify a bucket/bucketpath to skip s3 selection
    :type bucket: str, optional
    :param version: use a previous version of the template
    :type version: Union[bool, str], optional
    :raises NoNameEntered: when the new stack receive empty string as stack_name
    """
    cloudformation = Cloudformation(profile, region)

    if local_path:
        if type(local_path) != str:
            fzf = Pyfzf()
            local_path = str(
                fzf.get_local_file(search_from_root=root, cloudformation=True))
        cloudformation_args = construct_local_creation_args(
            cloudformation, str(local_path))
    else:
        cloudformation_args = construct_s3_creation_args(
            cloudformation, bucket, version)

    if extra:
        extra_args = CloudformationArgs(cloudformation)
        extra_args.set_extra_args(search_from_root=root)
        cloudformation_args.update(extra_args.extra_args)
    response = cloudformation.execute_with_capabilities(**cloudformation_args)

    response.pop("ResponseMetadata", None)
    print(json.dumps(response, indent=4, default=str))
    print(80 * "-")
    print("Stack creation initiated")

    if wait:
        cloudformation.stack_name = cloudformation_args["StackName"]
        cloudformation.wait("stack_create_complete",
                            "Waiting for stack to be ready ...")
        print("Stack created")
Exemplo n.º 7
0
 def setUp(self):
     fileloader = FileLoader()
     config_path = Path(__file__).resolve().parent.joinpath(
         "../data/fzfaws.yml")
     fileloader.load_config_file(config_path=str(config_path))
     self.capturedOutput = io.StringIO()
     sys.stdout = self.capturedOutput
     self.fzf = Pyfzf()
Exemplo n.º 8
0
    def set_encryption(self, original: str = None) -> None:
        """Set the encryption setting.

        :param original: previous value of the encryption
        :type original: str, optional
        """
        header = "select an ecryption setting, esc to use the default encryption setting for the bucket"
        if original:
            header += "\nOriginal: %s" % original

        fzf = Pyfzf()
        fzf.append_fzf("None (Use bucket default setting)\n")
        fzf.append_fzf("AES256\n")
        fzf.append_fzf("aws:kms\n")
        result: str = str(
            fzf.execute_fzf(empty_allow=True, print_col=1, header=header))
        if result:
            self._extra_args["ServerSideEncryption"] = result
        if result == "aws:kms":
            current_region = self.s3.client.get_bucket_location(
                Bucket=self.s3.bucket_name)
            current_region = current_region.get("LocationConstraint")
            kms = KMS(self.s3.profile, self.s3.region)
            kms.set_keyids(header="select encryption key to use")
            self._extra_args["SSEKMSKeyId"] = kms.keyids[0]
Exemplo n.º 9
0
    def set_ACL(self,
                original: bool = False,
                version: Optional[List[Dict[str, str]]] = None) -> None:
        """Set the ACL option.

        :param original: display original value
        :type original: bool, optional
        :param version: version object to set acl for version
        :type version: List[Dict[str, str]], optional
        """
        if not version:
            version = []

        fzf = Pyfzf()
        fzf.append_fzf("None (use bucket default ACL setting)\n")
        fzf.append_fzf(
            "Canned ACL (predefined set of grantees and permissions)\n")
        fzf.append_fzf(
            "Explicit ACL (explicit set grantees and permissions)\n")
        result = fzf.execute_fzf(
            empty_allow=True,
            print_col=1,
            header=
            "select a type of ACL to grant, aws accept one of canned ACL or explicit ACL",
        )
        if result == "Canned":
            self._set_canned_ACL()
        elif result == "Explicit":
            self._set_explicit_ACL(original=original, version=version)
        else:
            return
Exemplo n.º 10
0
    def __init__(
        self,
        profile: Optional[Union[str, bool]] = None,
        region: Optional[Union[str, bool]] = None,
        service_name: str = "",
    ) -> None:
        """Construct a BaseSession instance.

        If profile or region is True value, then
        fzf will be launched to let user select region or profile.
        """
        session = Session()
        selected_profile: Optional[str] = None
        selected_region: Optional[str] = None
        if profile and type(profile) == bool:
            fzf = Pyfzf()
            for profile in session.available_profiles:
                fzf.append_fzf("%s\n" % profile)
            selected_profile = str(fzf.execute_fzf(print_col=1))
        elif profile and type(profile) is str:
            selected_profile = str(profile)

        if region and type(region) == bool:
            fzf = Pyfzf()
            regions = session.get_available_regions(service_name)
            for region in regions:
                fzf.append_fzf("%s\n" % region)
            selected_region = str(fzf.execute_fzf(print_col=1))
        elif region and type(region) is str:
            selected_region = str(region)

        if not selected_profile:
            selected_profile = os.getenv(
                "FZFAWS_%s_PROFILE" % service_name.upper(), "")
            if not selected_profile:
                selected_profile = os.getenv("FZFAWS_GLOBAL_PROFILE", None)
        if not selected_region:
            selected_region = os.getenv(
                "FZFAWS_%s_REGION" % service_name.upper(), "")
            if not selected_region:
                selected_region = os.getenv("FZFAWS_GLOBAL_REGION", None)

        self.profile: Optional[str] = selected_profile
        self.region: Optional[str] = selected_region
        self.session = Session(region_name=selected_region,
                               profile_name=selected_profile)
        self._client = self.session.client(service_name)

        # only certain service support resource
        resources = self.session.get_available_resources()
        if service_name in resources:
            self._resource = self.session.resource(service_name)
Exemplo n.º 11
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))
Exemplo n.º 12
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))
Exemplo n.º 13
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)
Exemplo n.º 14
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)
Exemplo n.º 15
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"
        )
Exemplo n.º 16
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)
Exemplo n.º 17
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 = [{}]
Exemplo n.º 18
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)
Exemplo n.º 19
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)
Exemplo n.º 20
0
    def _get_capabilities(self, message: str = "") -> List[str]:
        """Display help message and let user select capabilities.

        :param message: message to display in fzf header
        :type message: str, optional
        :return: selected capabilities to acknowledge
        :rtype: List[str]
        """
        fzf = Pyfzf()
        fzf.append_fzf("CAPABILITY_IAM\n")
        fzf.append_fzf("CAPABILITY_NAMED_IAM\n")
        fzf.append_fzf("CAPABILITY_AUTO_EXPAND")
        message += "\nPlease select the capabilities to acknowledge and proceed"
        message += "\nMore information: https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_CreateStack.html"
        return list(
            fzf.execute_fzf(
                empty_allow=True, print_col=1, multi_select=True, header=message
            )
        )
Exemplo n.º 21
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))
Exemplo n.º 22
0
    def _set_explicit_ACL(
            self,
            original: bool = False,
            version: Optional[List[Dict[str, str]]] = None) -> None:
        """Set explicit ACL for grantees and permissions.

        Get user id/email first than display fzf allow multi_select
        to select permissions

        example version: [{"Key", key, "VersionId": versionid}]

        :param original: display original value
        :type original: bool, optional
        :param version: version of the object
        :type version: List[Dict[str, str]], optional
        """
        original_acl: Dict[str, List[str]] = {
            "FULL_CONTROL": [],
            "WRITE_ACP": [],
            "READ": [],
            "READ_ACP": [],
        }

        # get original values
        if original:
            acls = None
            if not version:
                acls = self.s3.client.get_object_acl(
                    Bucket=self.s3.bucket_name, Key=self.s3.path_list[0])
            elif len(version) == 1:
                acls = self.s3.client.get_object_acl(
                    Bucket=self.s3.bucket_name,
                    Key=self.s3.path_list[0],
                    VersionId=version[0].get("VersionId"),
                )
            if acls:
                owner = acls["Owner"]["ID"]
                for grantee in acls.get("Grants", []):
                    if grantee["Grantee"].get("EmailAddress"):
                        original_acl[grantee["Permission"]].append(
                            "%s=%s" % ("emailAddress",
                                       grantee["Grantee"].get("EmailAddress")))
                    elif (grantee["Grantee"].get("ID")
                          and grantee["Grantee"].get("ID") != owner):
                        original_acl[grantee["Permission"]].append(
                            "%s=%s" % ("id", grantee["Grantee"].get("ID")))
                    elif grantee["Grantee"].get("URI"):
                        original_acl[grantee["Permission"]].append(
                            "%s=%s" % ("uri", grantee["Grantee"].get("URI")))

                print("Current ACL:")
                print(json.dumps(original_acl, indent=4, default=str))
                print("Note: fzf.aws cannot preserve previous ACL permission")
                if not get_confirmation("Continue?"):
                    return

        # get what permission to set
        fzf = Pyfzf()
        fzf.append_fzf("GrantFullControl\n")
        fzf.append_fzf("GrantRead\n")
        fzf.append_fzf("GrantReadACP\n")
        fzf.append_fzf("GrantWriteACP\n")
        results: List[str] = list(
            fzf.execute_fzf(empty_allow=True, print_col=1, multi_select=True))
        if not results:
            print(
                "No permission is set, default ACL settings of the bucket would be used"
            )
        else:
            for result in results:
                print("Set permisstion for %s" % result)
                print(
                    "Enter a list of either the Canonical ID, Account email, Predefined Group url to grant permission (Seperate by comma)"
                )
                print(
                    "Format: id=XXX,id=XXX,[email protected],uri=http://acs.amazonaws.com/groups/global/AllUsers"
                )
                if original:
                    print(80 * "-")
                    if result == "GrantFullControl" and original_acl.get(
                            "FULL_CONTROL"):
                        print("Orignal: %s" %
                              ",".join(original_acl.get("FULL_CONTROL", [])))
                    elif result == "GrantRead" and original_acl.get("READ"):
                        print("Orignal: %s" %
                              ",".join(original_acl.get("READ", [])))
                    elif result == "GrantReadACP" and original_acl.get(
                            "READ_ACP"):
                        print("Orignal: %s" %
                              ",".join(original_acl.get("READ_ACP", [])))
                    elif result == "GrantWriteACP" and original_acl.get(
                            "WRITE_ACP"):
                        print("Orignal: %s" %
                              ",".join(original_acl.get("WRITE_ACP", [])))
                accounts = input("Accounts: ")
                print(80 * "-")
                self._extra_args[result] = str(accounts)
Exemplo n.º 23
0
    def _get_user_input(
        self,
        parameter_key: str,
        parameter_type: str,
        param_header: str,
        value_type: str = None,
        default: str = None,
    ) -> Union[str, List[str]]:
        """Get user input.

        :param parameter_key: the current parameter key to obtain user input
        :type parameter_key: str
        :param parameter_type: type of the parameter
        :type parameter_type: str
        :param param_header: information about current parameter
        :type param_header: str
        :param value_type: determine if the current action is update or new creation ('Default|Original')
        :type value_type: str, optional
        :param default: default value of params or orignal value
        :type default: str, optional
        :return: return the user selection through fzf or python input
        :rtype: Union[str, List[str]]
        """
        user_input: Union[str, List[str]] = ""

        # execute fzf if allowed_value array exists
        if "AllowedValues" in self.params[parameter_key]:
            param_header += self._print_parameter_key(parameter_key,
                                                      value_type, default)
            fzf = Pyfzf()
            for allowed_value in self.params[parameter_key]["AllowedValues"]:
                fzf.append_fzf("%s\n" % allowed_value)
            user_input = fzf.execute_fzf(empty_allow=True,
                                         print_col=1,
                                         header=param_header)
        else:
            if parameter_type in self._aws_specific_param:
                param_header += self._print_parameter_key(
                    parameter_key, value_type, default)
                user_input = self._get_selected_param_value(
                    parameter_type, param_header)
            elif parameter_type in self._aws_specific_list_param:
                param_header += self._print_parameter_key(
                    parameter_key, value_type, default)
                user_input = self._get_list_param_value(
                    parameter_type, param_header)
            else:
                print(param_header.rstrip())
                if not value_type:
                    user_input = input("%s: " % parameter_key)
                elif value_type == "Default":
                    user_input = input("%s(Default: %s): " %
                                       (parameter_key, default))
                elif value_type == "Original":
                    user_input = input("%s(Original: %s): " %
                                       (parameter_key, default))
        if not user_input and default:
            return default
        elif user_input == "''":
            return ""
        elif user_input == '""':
            return ""
        else:
            return user_input
Exemplo n.º 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
Exemplo n.º 25
0
 def _set_canned_ACL(self) -> None:
     """Set the canned ACL for the current operation."""
     fzf = Pyfzf()
     fzf.append_fzf("private\n")
     fzf.append_fzf("public-read\n")
     fzf.append_fzf("public-read-write\n")
     fzf.append_fzf("authenticated-read\n")
     fzf.append_fzf("aws-exec-read\n")
     fzf.append_fzf("bucket-owner-read\n")
     fzf.append_fzf("bucket-owner-full-control\n")
     result: str = str(
         fzf.execute_fzf(
             empty_allow=True,
             print_col=1,
             header=
             "select a Canned ACL option, esc to use the default ACL setting for the bucket",
         ))
     if result:
         self._extra_args["ACL"] = result
Exemplo n.º 26
0
def changeset_stack(
    profile: Union[str, bool] = False,
    region: Union[str, bool] = False,
    replace: bool = False,
    local_path: Union[str, bool] = False,
    root: bool = False,
    wait: bool = False,
    info: bool = False,
    execute: bool = False,
    delete: bool = False,
    extra: bool = False,
    bucket: str = None,
    version: Union[str, bool] = False,
) -> None:
    """Handle changeset actions.

    Main function to interacte with changeset, use argument
    to control the actions.

    This function is using update_stack to handle all the dirty
    works as both functions are processing cloudformation arguments
    and having the same arguments.

    :param profile: use a different profile for this operation
    :type profile: Union[bool, str], optional
    :param region: use a different region for this operation
    :type region: Union[bool, str], optional
    :param replace: replace the template during update
    :type replace: bool, optional
    :param local_path: Select a template from local machine
    :type local_path: Union[bool, str], optional
    :param root: Search local file from root directory
    :type root: bool, optional
    :param wait: wait for stack to be completed before exiting the program
    :type wait: bool, optional
    :param info: display result of a changeset
    :type info: bool, optional
    :param execute: execute changeset
    :type execute: bool, optional
    :param delete: delete changeset
    :type delete: bool, optional
    :param extra: configure extra options for the stack, (tags, IAM, termination protection etc..)
    :type extra: bool, optional
    :param bucket: specify a bucket/bucketpath to skip s3 selection
    :type bucket: str, optional
    :param version: use previous version of template in s3 bucket
    :type version: Union[bool, str], optional
    :raises NoNameEntered: If no changset name is entered
    """
    cloudformation = Cloudformation(profile, region)
    cloudformation.set_stack()

    # if not creating new changeset
    if info or execute or delete:
        fzf = Pyfzf()
        response: Dict[str, Any] = cloudformation.client.list_change_sets(
            StackName=cloudformation.stack_name)
        # get the changeset name
        fzf.process_list(
            response.get("Summaries", []),
            "ChangeSetName",
            "StackName",
            "ExecutionStatus",
            "Status",
            "Description",
        )

        if info:
            selected_changeset = str(fzf.execute_fzf())
            describe_changes(cloudformation, selected_changeset)

        # execute the change set
        elif execute:
            selected_changeset = fzf.execute_fzf()
            if get_confirmation("Execute changeset %s?" % selected_changeset):
                response = cloudformation.client.execute_change_set(
                    ChangeSetName=selected_changeset,
                    StackName=cloudformation.stack_name,
                )
                cloudformation.wait("stack_update_complete",
                                    "Wating for stack to be updated ...")
                print("Stack updated")

        elif delete:
            selected_changeset = fzf.execute_fzf(multi_select=True)
            for changeset in selected_changeset:
                print("(dryrun) Delete changeset %s" % changeset)
            if get_confirmation("Confirm?"):
                for changeset in selected_changeset:
                    cloudformation.client.delete_change_set(
                        ChangeSetName=changeset,
                        StackName=cloudformation.stack_name)

    else:
        changeset_name = input("Enter name of this changeset: ")
        if not changeset_name:
            raise NoNameEntered("No changeset name specified")
        changeset_description = input("Description: ")
        # since is almost same operation as update stack
        # let update_stack handle it, but return update details instead of execute
        cloudformation_args = update_stack(
            cloudformation.profile,
            cloudformation.region,
            replace,
            local_path,
            root,
            wait,
            extra,
            bucket,
            version,
            dryrun=True,
            cloudformation=cloudformation,
        )
        cloudformation_args[
            "cloudformation_action"] = cloudformation.client.create_change_set
        cloudformation_args["ChangeSetName"] = changeset_name
        if changeset_description:
            cloudformation_args["Description"] = changeset_description

        response = cloudformation.execute_with_capabilities(
            **cloudformation_args)

        response.pop("ResponseMetadata", None)
        print(json.dumps(response, indent=4, default=str))
        print(80 * "-")
        print("Changeset create initiated")

        if wait:
            cloudformation.wait(
                "change_set_create_complete",
                "Wating for changset to be created ...",
                ChangeSetName=changeset_name,
            )
            print("Changeset created")
            describe_changes(cloudformation, changeset_name)
Exemplo n.º 27
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"))
Exemplo n.º 28
0
def validate_stack(
    profile: Optional[Union[str, bool]] = False,
    region: Optional[Union[str, bool]] = False,
    local_path: Union[str, bool] = False,
    root: bool = False,
    bucket: str = None,
    version: Union[str, bool] = False,
    no_print: bool = False,
) -> None:
    """Validate the selected cloudformation template using boto3 api.

    This is also used internally by create_stack and update_stack
    operations.

    :param profile: Use a different profile for this operation
    :type profile: Union[bool, str], optional
    :param region: Use a different region for this operation
    :type region: Union[bool, str], optional
    :param local_path: Select a template from local machine
    :type local_path: Union[bool, str], optional
    :param root: Search local file from root directory
    :type root: bool, optional
    :param bucket: specify a bucket/bucketpath to skip s3 selection
    :type bucket: str, optional
    :param version: use a previous version of the template
    :type version: Union[bool, str], optional
    :param no_print: Don't print the response, only check excpetion
    :type no_print: bool, optional
    """
    cloudformation = Cloudformation(profile, region)

    if local_path:
        if type(local_path) != str:
            fzf = Pyfzf()
            local_path = str(
                fzf.get_local_file(
                    search_from_root=root,
                    cloudformation=True,
                    header="select a cloudformation template to validate",
                ))
        check_is_valid(local_path)
        with open(local_path, "r") as file_body:
            response = cloudformation.client.validate_template(
                TemplateBody=file_body.read())
    else:
        s3 = S3(profile, region)
        s3.set_bucket_and_path(bucket)
        if not s3.bucket_name:
            s3.set_s3_bucket(
                header="select a bucket which contains the template")
        if not s3.path_list[0]:
            s3.set_s3_object()

        check_is_valid(s3.path_list[0])

        if version == True:
            version = s3.get_object_version(s3.bucket_name,
                                            s3.path_list[0])[0].get(
                                                "VersionId", False)

        template_body_loacation = s3.get_object_url(
            "" if not version else str(version))
        response = cloudformation.client.validate_template(
            TemplateURL=template_body_loacation)

    if not no_print:
        response.pop("ResponseMetadata", None)
        print(json.dumps(response, indent=4, default=str))
Exemplo n.º 29
0
    def _get_path_option(self, download: bool = False) -> str:
        """Pop fzf for user to select what to do with the path.

        :param download: if not download, insert append option
        :type download: bool, optional
        :return: selected option
        :rtype: str
        """
        fzf = Pyfzf()
        fzf.append_fzf("root: operate on the root level of the bucket\n")
        fzf.append_fzf(
            "interactively: interactively select a path through s3\n")
        fzf.append_fzf("input: manully input the path/name\n")
        if not download:
            fzf.append_fzf(
                "append: interactively select a path and then input new path/name to append"
            )
        selected_option = str(
            fzf.execute_fzf(
                print_col=1,
                header=
                "Please select which level of the bucket would you like to operate in",
                delimiter=": ",
            ))
        return selected_option
Exemplo n.º 30
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=": "))