geofront.remote
— Remote sets¶
Every RemoteSet
is represented as a mapping (which is immutable,
or mutable) of alias str
to Remote
object e.g.:
{
'web-1': Remote('ubuntu', '192.168.0.5'),
'web-2': Remote('ubuntu', '192.168.0.6'),
'web-3': Remote('ubuntu', '192.168.0.7'),
'worker-1': Remote('ubuntu', '192.168.0.25'),
'worker-2': Remote('ubuntu', '192.168.0.26'),
'db-1': Remote('ubuntu', '192.168.0.50'),
'db-2': Remote('ubuntu', '192.168.0.51'),
}
However, in the age of the cloud, you don’t have to manage the remote set since the most of cloud providers offer their API to list provisioned remote nodes.
Geofront provides builtin CloudRemoteSet
,
a subtype of RemoteSet
(which is alias of
Mapping
[str
, Remote
]), that proxies to
the list dynamically made by cloud providers.
Changed in version 0.2.0: CloudRemoteSet
is moved from this module to
geofront.backends.cloud
.
See CloudRemoteSet
.
-
class
geofront.remote.
AuthorizedKeyList
(sftp_client: paramiko.sftp_client.SFTPClient) → None¶ List-like abstraction for remote
authorized_keys
.Note that the contents are all lazily evaluated, so in order to pretend heavy duplicate communications over SFTP use
list()
to eagerly evaluate e.g.:lazy_list = AuthorizedKeyList(sftp_client) eager_list = list(lazy_list) # ... some modifications on eager_list ... lazy_list[:] = eager_list
Parameters: sftp_client ( paramiko.sftp_client.SFTPClient
) – the remote sftp connection to accessauthorized_keys
-
class
geofront.remote.
DefaultPermissionPolicy
¶ All remotes are listed and allowed for everyone in the team.
New in version 0.2.0.
-
class
geofront.remote.
GroupMetadataPermissionPolicy
(metadata_key: str, separator: str = None) → None¶ Allow/disallow remotes according their metadata. It assumes every remote has a metadata key that stores a set of groups to allow. For example, suppose there’s the following remote set:
{ 'web-1': Remote('ubuntu', '192.168.0.5', metadata={'role': 'web'}), 'web-2': Remote('ubuntu', '192.168.0.6', metadata={'role': 'web'}), 'web-3': Remote('ubuntu', '192.168.0.7', metadata={'role': 'web'}), 'worker-1': Remote('ubuntu', '192.168.0.25', metadata={'role': 'worker'}), 'worker-2': Remote('ubuntu', '192.168.0.26', metadata={'role': 'worker'}), 'db-1': Remote('ubuntu', '192.168.0.50', metadata={'role': 'db'}), 'db-2': Remote('ubuntu', '192.168.0.51', metadata={'role': 'db'}) }
and there are groups identified as
'web'
,'worker'
, and'db'
. So the following policy would allow only members who belong to the corresponding groups:GroupMetadataPermissionPolicy(‘role’)Parameters: - metadata_key (
str
) – the key to find corresponding groups in metadata of each remote - separator (
str
) – the character separates multiple group identifiers in the metadata value. for example, if the groups are stored as like'sysadmin,owners'
then it should be','
. it splits group identifiers by all whitespace characters by default
New in version 0.2.0.
- metadata_key (
-
class
geofront.remote.
PermissionPolicy
¶ Permission policy determines which remotes are visible by a team member, and which remotes are allowed to SSH. So each remote can have one of three states for each team member:
- Listed and allowed
- A member can SSH to the remote.
- Listed but disallowed
- A member can be aware of the remote, but cannot SSH to it.
- Unlisted and disallowed
- A member can’t be aware of the remote, and can’t SSH to it either.
- Unlisted but allowed
- It is possible in theory, but mostly meaningless in practice.
The implementation of this interface has to implement two methods. One is
filter()
which determines whether remotes are listed or unlisted. Other one ispermit()
which determines whether remotes are allowed or disallowed to SSH.New in version 0.2.0.
-
filter
(remotes: typing.Mapping[str, geofront.remote.Remote], identity: geofront.identity.Identity, groups: typing.AbstractSet[collections.abc.Hashable]) → typing.Mapping[str, geofront.remote.Remote]¶ Determine which ones in the given
remotes
are visible to theidentity
(which belongs togroups
). The resulted mapping of filtered remotes has to be a subset of the inputremotes
.Parameters: Returns: the filtered result remote set
Return type:
-
permit
(remote: geofront.remote.Remote, identity: geofront.identity.Identity, groups: typing.AbstractSet[collections.abc.Hashable]) → bool¶ Determine whether to allow the given
identity
(which belongs togroups
) to SSH the givenremote
.Parameters:
-
class
geofront.remote.
Remote
(user: str, host: str, port: int = 22, metadata: typing.Mapping[str, object] = {}) → None¶ Remote node to SSH.
Parameters: New in version 0.2.0: Added optional
metadata
parameter.
-
geofront.remote.
RemoteSet
¶ The abstract type for remote sets. Keys are strings and values are
Remote
objects.Alias of
AbstractSet
[str
,Remote
].New in version 0.4.0.
alias of
Mapping
-
class
geofront.remote.
RemoteSetFilter
(filter: typing.Callable[[str, geofront.remote.Remote], bool], remote_set: typing.Mapping[str, geofront.remote.Remote]) → None¶ It takes a
filter
function and aremote_set
, and then return a filtered set of remotes.>>> remotes = { ... 'web-1': Remote('ubuntu', '192.168.0.5'), ... 'web-2': Remote('ubuntu', '192.168.0.6'), ... 'web-3': Remote('ubuntu', '192.168.0.7'), ... 'worker-1': Remote('ubuntu', '192.168.0.25'), ... 'worker-2': Remote('ubuntu', '192.168.0.26'), ... 'db-1': Remote('ubuntu', '192.168.0.50'), ... 'db-2': Remote('ubuntu', '192.168.0.51'), ... } >>> filtered = RemoteSetFilter( ... lambda a, r: a == 'web' or r.host.endswith('5'), ... remotes ... ) >>> dict(filtered) { 'web-1': Remote('ubuntu', '192.168.0.5'), 'web-2': Remote('ubuntu', '192.168.0.6'), 'web-3': Remote('ubuntu', '192.168.0.7'), 'worker-1': Remote('ubuntu', '192.168.0.25') }
The key difference of this and conditional dict comprehension is evaluation time. (TL;DR: the contents of
RemoteSetFilter
is evaluated everytime its filtered result is needed.)If
remote_set
is an ordinarydict
object,RemoteSetFilter
is not needed. But ifremote_set
is, for example,CloudRemoteSet
, the filtered result of dict comprehension on it is fixed at Geofront’s configuration loading time. That meansgeofront-cli remotes
doesn’t change even if the list of remotes in the cloud is changed.On the other hand, the filtered result of
RemoteSetFilter
is never fixed, because the filter onremote_set
is always evaluated again when its__iter__()
/__getitem__()
/etc are called.>>> remotes['web-4'] = Remote('ubuntu', '192.168.0.8') >>> del remotes['worker-1'] >>> dict(filtered) { 'web-1': Remote('ubuntu', '192.168.0.5'), 'web-2': Remote('ubuntu', '192.168.0.6'), 'web-3': Remote('ubuntu', '192.168.0.7'), 'web-4': Remote('ubuntu', '192.168.0.25'), # this replaced worker-1! }
Parameters: New in version 0.3.1.
-
class
geofront.remote.
RemoteSetUnion
(*remote_sets) → None¶ It takes two or more remote sets, and then return a union set of them. Note that the order of arguments affect overriding of aliases (keys). If there are any duplicated aliases (keys), the latter alias (key) is prior to the former.
>>> a = { ... 'web-1': Remote('ubuntu', '192.168.0.5'), ... 'web-2': Remote('ubuntu', '192.168.0.6'), ... 'web-3': Remote('ubuntu', '192.168.0.7'), ... 'worker-1': Remote('ubuntu', '192.168.0.8'), ... } >>> b = { ... 'worker-1': Remote('ubuntu', '192.168.0.25'), ... 'worker-2': Remote('ubuntu', '192.168.0.26'), ... 'db-1': Remote('ubuntu', '192.168.0.27'), ... 'db-2': Remote('ubuntu', '192.168.0.28'), ... 'db-3': Remote('ubuntu', '192.168.0.29'), ... } >>> c = { ... 'web-1': Remote('ubuntu', '192.168.0.49'), ... 'db-1': Remote('ubuntu', '192.168.0.50'), ... 'db-2': Remote('ubuntu', '192.168.0.51'), ... } >>> union = RemoteSetUnion(a, b, c) >>> dict(union) { 'web-1': Remote('ubuntu', '192.168.0.49'), 'web-2': Remote('ubuntu', '192.168.0.6'), 'web-3': Remote('ubuntu', '192.168.0.7'), 'worker-1': Remote('ubuntu', '192.168.0.25'), 'worker-2': Remote('ubuntu', '192.168.0.26'), 'db-1': Remote('ubuntu', '192.168.0.50'), 'db-2': Remote('ubuntu', '192.168.0.51'), 'db-3': Remote('ubuntu', '192.168.0.29'), }
Note that
RemoteSetUnion
is evaluated everytime its contents is needed, likeRemoteSetFilter
:>>> del c['web-1'] >>> dict(union) { 'web-1': Remote('ubuntu', '192.168.0.5'), # changed! 'web-2': Remote('ubuntu', '192.168.0.6'), 'web-3': Remote('ubuntu', '192.168.0.7'), 'worker-1': Remote('ubuntu', '192.168.0.25'), 'worker-2': Remote('ubuntu', '192.168.0.26'), 'db-1': Remote('ubuntu', '192.168.0.50'), 'db-2': Remote('ubuntu', '192.168.0.51'), 'db-3': Remote('ubuntu', '192.168.0.29'), }
Parameters: *remote_sets ( RemoteSet
) – two or more remote sets. every remote set has to be a mapping of aliasstr
toRemote
New in version 0.3.2.
Make an one-time authorization to the
remote
, and then revokes it whentimeout
reaches soon.Parameters: - public_keys (
AbstractSet
[paramiko.pkey.PKey
]) – the set of public keys to authorize - master_key (
paramiko.pkey.PKey
) – the master key (not owner’s key) - remote (
Remote
) – a remote to grant access permission - timeout (
datetime.timedelta
) – the time an authorization keeps alive
Returns: the expiration time
Return type: - public_keys (