Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions ENDPOINT_STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ File: `consul/api/agent.py`
| Endpoint | Python Method | Status | Missing Parameters |
| :--- | :--- | :--- | :--- |
| `GET /v1/agent/self` | `Agent.self` | ✅ | - |
| `GET /v1/agent/services` | `Agent.services` | ⚠️ | `filter` |
| `GET /v1/agent/services` | `Agent.services` | | - |
| `GET /v1/agent/service/:service_id` | `Agent.service_definition` | ✅ | - |
| `GET /v1/agent/checks` | `Agent.checks` | ⚠️ | `filter` |
| `GET /v1/agent/checks` | `Agent.checks` | | - |
| `GET /v1/agent/members` | `Agent.members` | ✅ | (`segment` is Ent) |
| `PUT /v1/agent/maintenance` | `Agent.maintenance` | ✅ | - |
| `PUT /v1/agent/join/:address` | `Agent.join` | ✅ | - |
| `PUT /v1/agent/leave` | `Agent.leave` | ✅ | - |
| `PUT /v1/agent/force-leave/:node` | `Agent.force_leave` | ⚠️ | `prune` (added v1.13) |
| `PUT /v1/agent/force-leave/:node` | `Agent.force_leave` | | - |
| `PUT /v1/agent/reload` | `Agent.reload` | ✅ | - |
| `GET /v1/agent/metrics` | `Agent.metrics` | ✅ | - |
| `GET /v1/agent/monitor` | `Agent.monitor` | ⚠️ | Not true streaming — single blocking read, see docstring |
Expand All @@ -45,7 +45,7 @@ File: `consul/api/agent.py`
### Agent Services
| Endpoint | Python Method | Status | Missing Parameters |
| :--- | :--- | :--- | :--- |
| `PUT /v1/agent/service/register` | `Agent.Service.register` | ⚠️ | Body: `Kind`, `Proxy`, `SocketPath`, `Locality` |
| `PUT /v1/agent/service/register` | `Agent.Service.register` | | - |
| `PUT /v1/agent/service/deregister/:id` | `Agent.Service.deregister` | ✅ | - |
| `PUT /v1/agent/service/maintenance/:id` | `Agent.Service.maintenance` | ✅ | - |

Expand Down Expand Up @@ -112,18 +112,18 @@ File: `consul/api/acl/*.py`
| `PUT /v1/acl/bootstrap` | `ACL.bootstrap` | ✅ | - |
| `POST /v1/acl/login` | `ACL.login` | ✅ | - |
| `POST /v1/acl/logout` | `ACL.logout` | ✅ | - |
| `GET /v1/acl/tokens` | `Token.list` | ⚠️ | `policy`, `role`, `authmethod`, `secondary` (filters) |
| `PUT /v1/acl/token` | `Token.create` | ⚠️ | `ServiceIdentities`, `NodeIdentities`, `ExpirationTime`, `ExpirationTTL`, `Local` |
| `GET /v1/acl/tokens` | `Token.list` | | - |
| `PUT /v1/acl/token` | `Token.create` | | - |
| `GET /v1/acl/token/:accessor` | `Token.read` | ✅ | - |
| `PUT /v1/acl/token/:accessor` | `Token.update` | ⚠️ | `ServiceIdentities`, `NodeIdentities`, `ExpirationTime`, `ExpirationTTL`, `Local` |
| `PUT /v1/acl/token/:accessor` | `Token.update` | | - |
| `DELETE /v1/acl/token/:accessor` | `Token.delete` | ✅ | - |
| `PUT /v1/acl/token/:accessor/clone` | `Token.clone` | ✅ | - |
| `GET /v1/acl/token/self` | `Token.read_self` | ✅ | - |
| `GET /v1/acl/policies` | `Policy.list` | ✅ | - |
| `PUT /v1/acl/policy` | `Policy.create` | ✅ | - |
| `GET /v1/acl/policy/:id` | `Policy.read` | ✅ | - |
| `PUT /v1/acl/policy/:id` | - | | - |
| `DELETE /v1/acl/policy/:id` | - | | - |
| `PUT /v1/acl/policy/:id` | `Policy.update` | | - |
| `DELETE /v1/acl/policy/:id` | `Policy.delete` | | - |
| `GET /v1/acl/roles` | `Role.list` | ✅ | - |
| `PUT /v1/acl/role` | `Role.create` | ✅ | - |
| `GET /v1/acl/role/:id` | `Role.read` | ✅ | - |
Expand Down Expand Up @@ -211,7 +211,7 @@ File: `consul/api/connect.py`
| :--- | :--- | :--- | :--- |
| `GET /v1/connect/ca/roots` | `Connect.CA.roots` | ✅ | - |
| `GET /v1/connect/ca/configuration` | `Connect.CA.configuration` | ✅ | - |
| `PUT /v1/connect/ca/configuration` | - | | - |
| `PUT /v1/connect/ca/configuration` | `Connect.CA.update_configuration` | | - |
| `PUT /v1/connect/intentions/exact` | `Connect.Intentions.upsert` | ✅ | - |
| `GET /v1/connect/intentions/exact` | `Connect.Intentions.read` | ✅ | - |
| `DELETE /v1/connect/intentions/exact` | `Connect.Intentions.delete` | ✅ | - |
Expand Down
35 changes: 35 additions & 0 deletions consul/api/acl/policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,38 @@ def create(self, name: str, token: str | None = None, description: str | None =
headers=headers,
data=json.dumps(json_data),
)

def update(self, uuid: str, name: str, token: str | None = None, description: str | None = None, rules=None):
"""
Update the policy identified by *uuid*. This is a privileged endpoint, and
requires a token with acl:write.
:param uuid: Specifies the UUID of the policy to update.
:param name: Specifies a name for the ACL policy.
:param token: token with acl:write capability
:param description: Free form human-readable description of the policy.
:param rules: Specifies rules for the ACL policy.
:return: The updated policy information
"""
json_data = {"ID": uuid, "Name": name}
if rules:
json_data["Rules"] = json.dumps(rules)
if description:
json_data["Description"] = description
headers = self.agent.prepare_headers(token)
return self.agent.http.put(
CB.json(),
f"/v1/acl/policy/{uuid}",
headers=headers,
data=json.dumps(json_data),
)

def delete(self, uuid: str, token: str | None = None) -> bool:
"""
Deletes the policy with *uuid*. This is a privileged endpoint, and requires a
token with acl:write.
:param uuid: Specifies the UUID of the policy to delete.
:param token: token with acl:write capability
:return: True if the policy was deleted
"""
headers = self.agent.prepare_headers(token)
return self.agent.http.delete(CB.boolean(), f"/v1/acl/policy/{uuid}", headers=headers)
135 changes: 101 additions & 34 deletions consul/api/acl/token.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,35 @@ def parse_identities(node_identities: list[str] | None) -> dict[str, list[dict[s
return {}


def _policy_links(policies_id: list[str] | None, policies_name: list[str] | None) -> list[dict[str, str]]:
links: list[dict[str, str]] = []
if policies_id:
links.extend({"ID": policy} for policy in policies_id)
if policies_name:
links.extend({"Name": policy} for policy in policies_name)
return links


def _role_links(roles_id: list[str] | None, roles_name: list[str] | None) -> list[dict[str, str]]:
links: list[dict[str, str]] = []
if roles_id:
links.extend({"ID": role} for role in roles_id)
if roles_name:
links.extend({"Name": role} for role in roles_name)
return links


def _templated_policy_entries(
templated_policies: list[dict[str, dict[str, str]]] | None,
) -> list[dict[str, Any]]:
entries: list[dict[str, Any]] = []
if templated_policies is not None:
for templated_policy in templated_policies:
for name, variables in templated_policy.items():
entries.append({"TemplateName": name, "TemplateVariables": variables})
return entries


class AclPolicyLink(TypedDict, total=False):
ID: str
Name: str
Expand All @@ -49,15 +78,36 @@ class Token:
def __init__(self, agent) -> None:
self.agent = agent

def list(self, token: str | None = None) -> builtins.list[AclToken]:
def list(
self,
policy: str | None = None,
role: str | None = None,
authmethod: str | None = None,
servicename: str | None = None,
token: str | None = None,
) -> builtins.list[AclToken]:
"""
Lists all the active ACL tokens. This is a privileged endpoint, and
requires a management token. *token* will override this client's
default token.
Requires a token with acl:read capability. ACLPermissionDenied raised otherwise
:param policy: Optional policy ID to filter the results by.
:param role: Optional role ID to filter the results by.
:param authmethod: Optional auth method name to filter the results by.
:param servicename: Optional service name to filter the results by
(matches tokens with a ServiceIdentity for this service).
"""
params: list[tuple[str, Any]] = []
if policy:
params.append(("policy", policy))
if role:
params.append(("role", role))
if authmethod:
params.append(("authmethod", authmethod))
if servicename:
params.append(("servicename", servicename))
headers = self.agent.prepare_headers(token)
return self.agent.http.get(CB.json(), "/v1/acl/tokens", headers=headers)
return self.agent.http.get(CB.json(), "/v1/acl/tokens", params=params, headers=headers)

def read(self, accessor_id: str, token: str | None = None) -> AclToken:
"""
Expand Down Expand Up @@ -114,12 +164,16 @@ def create(
accessor_id: str | None = None,
secret_id: str | None = None,
node_identities: builtins.list[str] | None = None,
service_identities: builtins.list[builtins.dict[str, Any]] | None = None,
policies_id: builtins.list[str] | None = None,
description: str = "",
policies_name: builtins.list[str] | None = None,
roles_id: builtins.list[str] | None = None,
roles_name: builtins.list[str] | None = None,
templated_policies: builtins.list[builtins.dict[str, builtins.dict[str, str]]] | None = None,
expiration_time: str | None = None,
expiration_ttl: str | None = None,
local: bool | None = None,
) -> AclToken:
"""
Create a token (optionally identified by *secret_id* and *accessor_id*).
Expand All @@ -128,11 +182,19 @@ def create(
:param accessor_id: The accessor ID of the token to create
:param secret_id: The secret ID of the token to create
:param node_identities: Optional list of node identities (format: 'nodename:datacenter'), requires consul>=1.8.1
:param service_identities: Optional list of service identities, e.g.
[{"ServiceName": "web", "Datacenters": ["dc1"]}] ("Datacenters" is optional)
:param description: Optional new token description
:param policies_id: Optional list of policies id
:param roles_id: Optional list of roles id
:param roles_name: Optional list of roles name
:param templated_policies: Optional list of templated policies,
:param expiration_time: Optional absolute expiration timestamp (RFC3339), 1 minute
to 24 hours in the future. Mutually exclusive with expiration_ttl.
:param expiration_ttl: Optional expiration duration (e.g. "1h") relative to the
token's creation time, 1 minute to 24 hours. Mutually exclusive with expiration_time.
:param local: Optional, if True the token is local to this datacenter and not
replicated globally.
:return: The cloned token information
"""

Expand All @@ -143,31 +205,28 @@ def create(
json_data["SecretID"] = secret_id
if description:
json_data["Description"] = description
if service_identities is not None:
json_data["ServiceIdentities"] = service_identities
if expiration_time:
json_data["ExpirationTime"] = expiration_time
if expiration_ttl:
json_data["ExpirationTTL"] = expiration_ttl
if local is not None:
json_data["Local"] = local

json_data.update(parse_identities(node_identities))

policies: list[dict[str, str]] = []
if policies_id:
policies.extend({"ID": policy} for policy in policies_id)
if policies_name:
policies.extend({"Name": policy} for policy in policies_name)
policies = _policy_links(policies_id, policies_name)
if policies:
json_data["Policies"] = policies

roles: list[dict[str, str]] = []
if roles_id:
roles.extend({"ID": role} for role in roles_id)
if roles_name:
roles.extend({"Name": role} for role in roles_name)
roles = _role_links(roles_id, roles_name)
if roles:
json_data["Roles"] = roles

if templated_policies is not None:
json_data["TemplatedPolicies"] = []
for templated_policy in templated_policies:
for name, variables in templated_policy.items():
policy_dict = {"TemplateName": name, "TemplateVariables": variables}
json_data["TemplatedPolicies"].append(policy_dict)
templated_policy_entries = _templated_policy_entries(templated_policies)
if templated_policy_entries:
json_data["TemplatedPolicies"] = templated_policy_entries

headers = self.agent.prepare_headers(token)
return self.agent.http.put(
Expand All @@ -183,12 +242,16 @@ def update(
token: str | None = None,
secret_id: str | None = None,
node_identities: builtins.list[str] | None = None,
service_identities: builtins.list[builtins.dict[str, Any]] | None = None,
description: str = "",
policies_id: builtins.list[str] | None = None,
policies_name: builtins.list[str] | None = None,
roles_id: builtins.list[str] | None = None,
roles_name: builtins.list[str] | None = None,
templated_policies: builtins.list[builtins.dict[str, builtins.dict[str, str]]] | None = None,
expiration_time: str | None = None,
expiration_ttl: str | None = None,
local: bool | None = None,
) -> AclToken:
"""
Update a token (optionally identified by *secret_id* and *accessor_id*).
Expand All @@ -197,11 +260,18 @@ def update(
:param token: token with acl:write capability
:param secret_id: Optional secret ID of the token to update
:param node_identities: Optional list of node identities (format: 'nodename:datacenter'), requires consul>=1.8.1
:param service_identities: Optional list of service identities, e.g.
[{"ServiceName": "web", "Datacenters": ["dc1"]}] ("Datacenters" is optional)
:param description: Optional new token description
:param policies_id: Optional list of policies id
:param roles_id: Optional list of roles id
:param roles_name: Optional list of roles name
:param templated_policies: Optional list of templated policies
:param expiration_time: Optional absolute expiration timestamp (RFC3339). Note
Consul does not allow extending a token's expiration once set.
:param expiration_ttl: Optional expiration duration (e.g. "1h") relative to the
token's creation time. Mutually exclusive with expiration_time.
:param local: Optional, if True the token is local to this datacenter.
:return: The updated token information
"""

Expand All @@ -211,31 +281,28 @@ def update(
json_data["SecretID"] = secret_id
if description:
json_data["Description"] = description
if service_identities is not None:
json_data["ServiceIdentities"] = service_identities
if expiration_time:
json_data["ExpirationTime"] = expiration_time
if expiration_ttl:
json_data["ExpirationTTL"] = expiration_ttl
if local is not None:
json_data["Local"] = local

json_data.update(parse_identities(node_identities))

policies: list[dict[str, str]] = []
if policies_id:
policies.extend({"ID": policy} for policy in policies_id)
if policies_name:
policies.extend({"Name": policy} for policy in policies_name)
policies = _policy_links(policies_id, policies_name)
if policies:
json_data["Policies"] = policies

roles: list[dict[str, str]] = []
if roles_id:
roles.extend({"ID": role} for role in roles_id)
if roles_name:
roles.extend({"Name": role} for role in roles_name)
roles = _role_links(roles_id, roles_name)
if roles:
json_data["Roles"] = roles

if templated_policies is not None:
json_data["TemplatedPolicies"] = []
for templated_policy in templated_policies:
for name, variables in templated_policy.items():
policy_dict = {"TemplateName": name, "TemplateVariables": variables}
json_data["TemplatedPolicies"].append(policy_dict)
templated_policy_entries = _templated_policy_entries(templated_policies)
if templated_policy_entries:
json_data["TemplatedPolicies"] = templated_policy_entries

headers = self.agent.prepare_headers(token)
return self.agent.http.put(
Expand Down
Loading
Loading