mirror of
https://gitlab.sectorq.eu/jaydee/portainer.git
synced 2025-12-14 02:34:53 +01:00
build
This commit is contained in:
278
port.py
278
port.py
@@ -20,8 +20,9 @@ class Portainer:
|
||||
Instantiate with base_url and optional token/timeout and call methods
|
||||
to perform API operations.
|
||||
"""
|
||||
|
||||
def __init__(self, base_url, token, timeout=10):
|
||||
self.base_url = base_url.rstrip('/')
|
||||
self.base_url = base_url.rstrip("/")
|
||||
self.token = token
|
||||
self.timeout = timeout
|
||||
self.git_url = "git@gitlab.sectorq.eu:home/docker-compose.git"
|
||||
@@ -43,7 +44,7 @@ class Portainer:
|
||||
"bitwarden",
|
||||
"mailu3",
|
||||
"home-assistant",
|
||||
"homepage"
|
||||
"homepage",
|
||||
]
|
||||
self.nas_stacks = self.basic_stacks + [
|
||||
"gitlab",
|
||||
@@ -54,42 +55,38 @@ class Portainer:
|
||||
"immich",
|
||||
"jupyter",
|
||||
"kestra",
|
||||
"mealie"
|
||||
"mealie",
|
||||
]
|
||||
self.m_server_stacks = self.basic_stacks + [
|
||||
'immich',
|
||||
'zabbix-server',
|
||||
'gitea',
|
||||
'unifibrowser',
|
||||
'mediacenter',
|
||||
'watchtower',
|
||||
'wazuh',
|
||||
'octoprint',
|
||||
'motioneye',
|
||||
'kestra',
|
||||
'bookstack',
|
||||
'wud',
|
||||
'uptime-kuma',
|
||||
'registry',
|
||||
'regsync',
|
||||
'dockermon',
|
||||
'grafana',
|
||||
'nextcloud',
|
||||
'semaphore',
|
||||
'node-red',
|
||||
'test',
|
||||
'jupyter',
|
||||
'paperless',
|
||||
'mealie',
|
||||
'n8n',
|
||||
'ollama',
|
||||
'rancher'
|
||||
]
|
||||
self.rpi5_stacks = self.basic_stacks + [
|
||||
"gitlab",
|
||||
"immich",
|
||||
"zabbix-server",
|
||||
"gitea",
|
||||
"unifibrowser",
|
||||
"mediacenter",
|
||||
"watchtower",
|
||||
"wazuh",
|
||||
"octoprint",
|
||||
"motioneye",
|
||||
"kestra",
|
||||
"bookstack",
|
||||
"gitea"
|
||||
"wud",
|
||||
"uptime-kuma",
|
||||
"registry",
|
||||
"regsync",
|
||||
"dockermon",
|
||||
"grafana",
|
||||
"nextcloud",
|
||||
"semaphore",
|
||||
"node-red",
|
||||
"test",
|
||||
"jupyter",
|
||||
"paperless",
|
||||
"mealie",
|
||||
"n8n",
|
||||
"ollama",
|
||||
"rancher",
|
||||
]
|
||||
self.rpi5_stacks = self.basic_stacks + ["gitlab", "bookstack", "gitea"]
|
||||
self.rack_stacks = self.basic_stacks + [
|
||||
"gitlab",
|
||||
"bookstack",
|
||||
@@ -99,7 +96,7 @@ class Portainer:
|
||||
"immich",
|
||||
"jupyter",
|
||||
"kestra",
|
||||
"mealie"
|
||||
"mealie",
|
||||
]
|
||||
self.log_mode = False
|
||||
self.hw_mode = False
|
||||
@@ -136,13 +133,11 @@ class Portainer:
|
||||
"""Example authenticated GET request to Portainer API."""
|
||||
url = f"{self.base_url.rstrip('/')}{path}"
|
||||
headers = {"X-API-Key": f"{self.token}"}
|
||||
data = {
|
||||
"EndpointId": endpoint_id,
|
||||
"Name": name,
|
||||
"Env": json.dumps(envs)
|
||||
}
|
||||
data = {"EndpointId": endpoint_id, "Name": name, "Env": json.dumps(envs)}
|
||||
# print(data)
|
||||
resp = requests.post(url, headers=headers, files=file, data=data, timeout=timeout)
|
||||
resp = requests.post(
|
||||
url, headers=headers, files=file, data=data, timeout=timeout
|
||||
)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
|
||||
@@ -182,36 +177,49 @@ class Portainer:
|
||||
for s in stacks:
|
||||
# print(type(s["AutoUpdate"]) )
|
||||
# input(s)
|
||||
if s['EndpointId'] in fail_endponts:
|
||||
if s["EndpointId"] in fail_endponts:
|
||||
continue
|
||||
if not s['EndpointId'] in webhooks:
|
||||
if not s["EndpointId"] in webhooks:
|
||||
try:
|
||||
webhooks[s['EndpointId']] = {"webhook": {}}
|
||||
webhooks[self.endpoints["by_id"][s['EndpointId']]] = {"webhook": {}}
|
||||
webhooks[s["EndpointId"]] = {"webhook": {}}
|
||||
webhooks[self.endpoints["by_id"][s["EndpointId"]]] = {"webhook": {}}
|
||||
except Exception as e:
|
||||
logger.debug(f"Exception while getting webhooks for endpoint {s['EndpointId']}: {e}")
|
||||
if not s['EndpointId'] in self.stacks_all:
|
||||
self.stacks_all[s['EndpointId']] = {"by_id": {}, "by_name": {}}
|
||||
self.stacks_all[self.endpoints["by_id"][s['EndpointId']]] = {"by_id": {}, "by_name": {}}
|
||||
self.stacks_all[s['EndpointId']]["by_id"][s['Id']] = s['Name']
|
||||
self.stacks_all[self.endpoints["by_id"][s['EndpointId']]]["by_id"][s['Id']] = s['Name']
|
||||
logger.debug(
|
||||
f"Exception while getting webhooks for endpoint {s['EndpointId']}: {e}"
|
||||
)
|
||||
if not s["EndpointId"] in self.stacks_all:
|
||||
self.stacks_all[s["EndpointId"]] = {"by_id": {}, "by_name": {}}
|
||||
self.stacks_all[self.endpoints["by_id"][s["EndpointId"]]] = {
|
||||
"by_id": {},
|
||||
"by_name": {},
|
||||
}
|
||||
self.stacks_all[s["EndpointId"]]["by_id"][s["Id"]] = s["Name"]
|
||||
self.stacks_all[self.endpoints["by_id"][s["EndpointId"]]]["by_id"][
|
||||
s["Id"]
|
||||
] = s["Name"]
|
||||
|
||||
self.stacks_all[s['EndpointId']]["by_name"][s['Name']] = s['Id']
|
||||
self.stacks_all[self.endpoints["by_id"][s['EndpointId']]]["by_name"][s['Name']] = s['Id']
|
||||
self.stacks_all[s["EndpointId"]]["by_name"][s["Name"]] = s["Id"]
|
||||
self.stacks_all[self.endpoints["by_id"][s["EndpointId"]]]["by_name"][
|
||||
s["Name"]
|
||||
] = s["Id"]
|
||||
# print(s)
|
||||
|
||||
if "AutoUpdate" in s and s["AutoUpdate"] is not None:
|
||||
if type(s["AutoUpdate"]) is dict and "Webhook" in s["AutoUpdate"]:
|
||||
# print(self.endpoints["by_id"][s['EndpointId']], s['Name'], s["AutoUpdate"]['Webhook'])
|
||||
# print("WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW")
|
||||
webhooks[s['EndpointId']][s['Name']] = s['AutoUpdate']['Webhook']
|
||||
webhooks[self.endpoints["by_id"][s['EndpointId']]][s['Name']] = s['AutoUpdate']['Webhook']
|
||||
webhooks[s["EndpointId"]][s["Name"]] = s["AutoUpdate"]["Webhook"]
|
||||
webhooks[self.endpoints["by_id"][s["EndpointId"]]][s["Name"]] = s[
|
||||
"AutoUpdate"
|
||||
]["Webhook"]
|
||||
elif s["AutoUpdate"]["Webhook"] != "":
|
||||
webhooks[s['EndpointId']][s['Name']] = s['Webhook']
|
||||
webhooks[self.endpoints["by_id"][s['EndpointId']]][s['Name']] = s['Webhook']
|
||||
webhooks[s["EndpointId"]][s["Name"]] = s["Webhook"]
|
||||
webhooks[self.endpoints["by_id"][s["EndpointId"]]][s["Name"]] = s[
|
||||
"Webhook"
|
||||
]
|
||||
|
||||
# print(self.stacks_all)
|
||||
if s['EndpointId'] == endpoint_id or endpoint_id == "all":
|
||||
if s["EndpointId"] == endpoint_id or endpoint_id == "all":
|
||||
stcks.append(s)
|
||||
# print(stcks)
|
||||
if stcks is None:
|
||||
@@ -269,7 +277,7 @@ class Portainer:
|
||||
for e in self.all_data["stacks"][s]["by_name"]:
|
||||
path = (
|
||||
f"/endpoints/{s}/docker/containers/json"
|
||||
f"?all=1&filters={{\"label\": [\"com.docker.compose.project={e}\"]}}"
|
||||
f'?all=1&filters={{"label": ["com.docker.compose.project={e}"]}}'
|
||||
)
|
||||
logging.info(f"request : {path}")
|
||||
try:
|
||||
@@ -285,11 +293,13 @@ class Portainer:
|
||||
if self.all_data["endpoints"]["by_id"][s] in data:
|
||||
data[self.all_data["endpoints"]["by_id"][s]][e] = contr
|
||||
else:
|
||||
data[self.all_data["endpoints"]["by_id"][s]] = {e: contr}
|
||||
data[self.all_data["endpoints"]["by_id"][s]] = {
|
||||
e: contr
|
||||
}
|
||||
except Exception as e:
|
||||
logger.debug(
|
||||
f"Exception while getting containers for stack {e} ",
|
||||
f"on endpoint {self.all_data['endpoints']['by_id'][s]}: {e}"
|
||||
f"on endpoint {self.all_data['endpoints']['by_id'][s]}: {e}",
|
||||
)
|
||||
# print(data)
|
||||
self.all_data["containers"] = data
|
||||
@@ -307,10 +317,9 @@ class Portainer:
|
||||
|
||||
def stop(c):
|
||||
print(f" > Stopping {c}")
|
||||
self.api_post_no_body(
|
||||
f"/endpoints/{ep_id}/docker/containers/{c}/stop"
|
||||
)
|
||||
self.api_post_no_body(f"/endpoints/{ep_id}/docker/containers/{c}/stop")
|
||||
# print(f"✔")
|
||||
|
||||
with ThreadPoolExecutor(max_workers=10) as exe:
|
||||
exe.map(stop, containers)
|
||||
# for c in containers:
|
||||
@@ -323,9 +332,8 @@ class Portainer:
|
||||
|
||||
def stop(c):
|
||||
print(f" > Starting {c}")
|
||||
self.api_post_no_body(
|
||||
f"/endpoints/{ep_id}/docker/containers/{c}/start"
|
||||
)
|
||||
self.api_post_no_body(f"/endpoints/{ep_id}/docker/containers/{c}/start")
|
||||
|
||||
with ThreadPoolExecutor(max_workers=10) as exe:
|
||||
exe.map(stop, containers)
|
||||
|
||||
@@ -340,10 +348,10 @@ class Portainer:
|
||||
# input(stcs)
|
||||
def update(c):
|
||||
print(f" > Updating {c[0]} on {endpoint}")
|
||||
ans = self.api_post_no_body(
|
||||
f"/stacks/webhooks/{c[1]}"
|
||||
ans = self.api_post_no_body(f"/stacks/webhooks/{c[1]}")
|
||||
logger.debug(
|
||||
f"Update response for stack {c[0]} on endpoint {endpoint}: {ans}"
|
||||
)
|
||||
logger.debug(f"Update response for stack {c[0]} on endpoint {endpoint}: {ans}")
|
||||
|
||||
def stop():
|
||||
cont = []
|
||||
@@ -368,10 +376,10 @@ class Portainer:
|
||||
eps = {"by_id": {}, "by_name": {}}
|
||||
eps_stats = {}
|
||||
for ep in endpoints:
|
||||
eps['by_id'][ep['Id']] = ep['Name']
|
||||
eps['by_name'][ep['Name']] = ep['Id']
|
||||
eps_stats[ep['Id']] = ep['Status']
|
||||
eps_stats[ep['Name']] = ep['Status']
|
||||
eps["by_id"][ep["Id"]] = ep["Name"]
|
||||
eps["by_name"][ep["Name"]] = ep["Id"]
|
||||
eps_stats[ep["Id"]] = ep["Status"]
|
||||
eps_stats[ep["Name"]] = ep["Status"]
|
||||
self.endpoints = eps
|
||||
self.all_data["endpoints"] = eps
|
||||
self.all_data["endpoints_status"] = eps_stats
|
||||
@@ -394,7 +402,7 @@ class Portainer:
|
||||
ep_id = self.endpoints["by_name"][endpoint]
|
||||
path = f"/endpoints/{ep_id}/docker/info"
|
||||
stats = self.api_get(path)
|
||||
return stats['Swarm']['Cluster']['ID']
|
||||
return stats["Swarm"]["Cluster"]["ID"]
|
||||
|
||||
def get_stack(self, stack=None, endpoint_id=None, timeout=None):
|
||||
self.get_stacks(endpoint_id)
|
||||
@@ -404,7 +412,7 @@ class Portainer:
|
||||
if stack == "all":
|
||||
for s in self.stacks:
|
||||
# print(s)
|
||||
if (endpoint_id == s.get("EndpointId")):
|
||||
if endpoint_id == s.get("EndpointId"):
|
||||
self.stack_ids.append(s.get("Id"))
|
||||
return self.stack_ids
|
||||
else:
|
||||
@@ -416,10 +424,9 @@ class Portainer:
|
||||
and endpoint_id == s.get("EndpointId")
|
||||
)
|
||||
|
||||
match_by_name = (
|
||||
str(s.get("Name")) == str(stack)
|
||||
and endpoint_id == int(s.get("EndpointId")) # Ensure types match for comparison
|
||||
)
|
||||
match_by_name = str(s.get("Name")) == str(stack) and endpoint_id == int(
|
||||
s.get("EndpointId")
|
||||
) # Ensure types match for comparison
|
||||
|
||||
if match_by_id or match_by_name:
|
||||
# if (stack is not None and s.get("Id") == stack and endpoint_id == s.get("EndpointId"))
|
||||
@@ -431,7 +438,15 @@ class Portainer:
|
||||
|
||||
raise ValueError(f"Stack not found: {stack}")
|
||||
|
||||
def create_stack(self, endpoint, stack=None, mode="git", autostart=False, swarm=False, timeout=None):
|
||||
def create_stack(
|
||||
self,
|
||||
endpoint,
|
||||
stack=None,
|
||||
mode="git",
|
||||
autostart=False,
|
||||
swarm=False,
|
||||
timeout=None,
|
||||
):
|
||||
if swarm:
|
||||
swarm_id = self.get_swarm_id(endpoint)
|
||||
p = "swarm"
|
||||
@@ -469,8 +484,8 @@ class Portainer:
|
||||
|
||||
# Check if the stack exists by ID or name
|
||||
stack_check = (
|
||||
stack in self.stacks_all[self.endpoint_id]['by_id']
|
||||
or stack in self.stacks_all[self.endpoint_id]['by_name']
|
||||
stack in self.stacks_all[self.endpoint_id]["by_id"]
|
||||
or stack in self.stacks_all[self.endpoint_id]["by_name"]
|
||||
)
|
||||
if stack_check:
|
||||
print(f"Stack {stack} already exist")
|
||||
@@ -488,24 +503,24 @@ class Portainer:
|
||||
name, value = ev.split("=", 1)
|
||||
envs.append({"name": name, "value": value})
|
||||
f.close()
|
||||
# wl(envs)
|
||||
# wl(envs)
|
||||
for e in envs:
|
||||
# print(f"Env: {e['name']} = {e['value']}")
|
||||
HWS = ["HW_MODE", "HW_MODE1", "HW_MODE2"]
|
||||
if e['name'] == "RESTART" and self.endpoint_name == "m-server":
|
||||
e['value'] = "always"
|
||||
if e['name'] in HWS:
|
||||
if e["name"] == "RESTART" and self.endpoint_name == "m-server":
|
||||
e["value"] = "always"
|
||||
if e["name"] in HWS:
|
||||
# print("Found HW_MODE env var.")
|
||||
if self.hw_mode:
|
||||
e['value'] = "hw"
|
||||
e["value"] = "hw"
|
||||
else:
|
||||
e['value'] = "cpu"
|
||||
if e['name'] == "LOGGING":
|
||||
e["value"] = "cpu"
|
||||
if e["name"] == "LOGGING":
|
||||
# print("Found LOGGING env var.")
|
||||
if self.log_mode:
|
||||
e['value'] = "journald"
|
||||
e["value"] = "journald"
|
||||
else:
|
||||
e['value'] = "syslog"
|
||||
e["value"] = "syslog"
|
||||
|
||||
uid = uuid.uuid4()
|
||||
# print(uid)
|
||||
@@ -516,7 +531,7 @@ class Portainer:
|
||||
"AutoUpdate": {
|
||||
"forcePullImage": True,
|
||||
"forceUpdate": False,
|
||||
"webhook": f"{uid}"
|
||||
"webhook": f"{uid}",
|
||||
},
|
||||
"repositoryURL": "https://gitlab.sectorq.eu/home/docker-compose.git",
|
||||
"ReferenceName": "refs/heads/main",
|
||||
@@ -539,7 +554,7 @@ class Portainer:
|
||||
"RegistryID": 4,
|
||||
"isDetachedFromGit": True,
|
||||
"method": "repository",
|
||||
"swarmID": None
|
||||
"swarmID": None,
|
||||
}
|
||||
if swarm:
|
||||
req["type"] = "swarm"
|
||||
@@ -564,11 +579,16 @@ class Portainer:
|
||||
created = True
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"Waiting for stack {stack} to be created...{tries}/50", end="\r")
|
||||
print(
|
||||
f"Waiting for stack {stack} to be created...{tries}/50",
|
||||
end="\r",
|
||||
)
|
||||
time.sleep(10)
|
||||
tries += 1
|
||||
if tries > 50:
|
||||
print(f"Error retrieving stack {stack} after creation: {self.endpoint_name}")
|
||||
print(
|
||||
f"Error retrieving stack {stack} after creation: {self.endpoint_name}"
|
||||
)
|
||||
break
|
||||
logger.debug(f"Exception while getting stack {stack}: {e}")
|
||||
|
||||
@@ -611,28 +631,31 @@ class Portainer:
|
||||
name, value = ev.split("=", 1)
|
||||
envs.append({"name": name, "value": value})
|
||||
f.close()
|
||||
# wl(envs)
|
||||
# wl(envs)
|
||||
for e in envs:
|
||||
# print(f"Env: {e['name']} = {e['value']}")
|
||||
HWS = ["HW_MODE", "HW_MODE1", "HW_MODE2"]
|
||||
if e['name'] == "RESTART" and self.endpoint_name == "m-server":
|
||||
e['value'] = "always"
|
||||
if e['name'] in HWS:
|
||||
if e["name"] == "RESTART" and self.endpoint_name == "m-server":
|
||||
e["value"] = "always"
|
||||
if e["name"] in HWS:
|
||||
print("Found HW_MODE env var.")
|
||||
if self.hw_mode:
|
||||
e['value'] = "hw"
|
||||
e["value"] = "hw"
|
||||
else:
|
||||
e['value'] = "cpu"
|
||||
if e['name'] == "LOGGING":
|
||||
e["value"] = "cpu"
|
||||
if e["name"] == "LOGGING":
|
||||
print("Found LOGGING env var.")
|
||||
if self.log_mode:
|
||||
e['value'] = "journald"
|
||||
e["value"] = "journald"
|
||||
else:
|
||||
e['value'] = "syslog"
|
||||
e["value"] = "syslog"
|
||||
|
||||
file = {
|
||||
# ("filename", file_object)
|
||||
"file": ("docker-compose.yml", open(f"/tmp/docker-compose/{stack}/docker-compose.yml", "rb")),
|
||||
"file": (
|
||||
"docker-compose.yml",
|
||||
open(f"/tmp/docker-compose/{stack}/docker-compose.yml", "rb"),
|
||||
),
|
||||
}
|
||||
self.api_post_file(path, self.endpoint_id, stack, envs, file)
|
||||
|
||||
@@ -642,16 +665,24 @@ class Portainer:
|
||||
data = []
|
||||
for stack in stacks:
|
||||
if endpoint is not None:
|
||||
if not stack['EndpointId'] in self.endpoints['by_id']:
|
||||
if not stack["EndpointId"] in self.endpoints["by_id"]:
|
||||
continue
|
||||
if endpoint != "all":
|
||||
if self.endpoints['by_name'][endpoint] != stack['EndpointId']:
|
||||
if self.endpoints["by_name"][endpoint] != stack["EndpointId"]:
|
||||
continue
|
||||
try:
|
||||
data.append([stack['Id'], stack['Name'], self.endpoints['by_id'][stack['EndpointId']]])
|
||||
data.append(
|
||||
[
|
||||
stack["Id"],
|
||||
stack["Name"],
|
||||
self.endpoints["by_id"][stack["EndpointId"]],
|
||||
]
|
||||
)
|
||||
except KeyError as e:
|
||||
data.append([stack['Id'], stack['Name'], "?"])
|
||||
logger.debug(f"KeyError getting endpoint name for stack {stack['Name']}: {e}")
|
||||
data.append([stack["Id"], stack["Name"], "?"])
|
||||
logger.debug(
|
||||
f"KeyError getting endpoint name for stack {stack['Name']}: {e}"
|
||||
)
|
||||
count += 1
|
||||
|
||||
headers = ["StackID", "Name", "Endpoint"]
|
||||
@@ -674,9 +705,13 @@ class Portainer:
|
||||
print(f"Error stoping stack: {e}")
|
||||
return []
|
||||
if "Id" in json.loads(resp):
|
||||
print(f"Stack {self.stacks_all[self.endpoint_id]['by_id'][stack]} : started")
|
||||
print(
|
||||
f"Stack {self.stacks_all[self.endpoint_id]['by_id'][stack]} : started"
|
||||
)
|
||||
else:
|
||||
print(f"Stack {self.stacks_all[self.endpoint_id]['by_id'][stack]} : {json.loads(resp)['message']}")
|
||||
print(
|
||||
f"Stack {self.stacks_all[self.endpoint_id]['by_id'][stack]} : {json.loads(resp)['message']}"
|
||||
)
|
||||
return True
|
||||
|
||||
def stop_stack(self, stack, endpoint_id):
|
||||
@@ -698,9 +733,13 @@ class Portainer:
|
||||
print(f"Error stopping stack: {e}")
|
||||
return []
|
||||
if "Id" in json.loads(resp):
|
||||
print(f"Stack {self.stacks_all[self.endpoint_id]['by_id'][stack]} : stopped")
|
||||
print(
|
||||
f"Stack {self.stacks_all[self.endpoint_id]['by_id'][stack]} : stopped"
|
||||
)
|
||||
else:
|
||||
print(f"Stack {self.stacks_all[self.endpoint_id]['by_id'][stack]} : {json.loads(resp)['message']}")
|
||||
print(
|
||||
f"Stack {self.stacks_all[self.endpoint_id]['by_id'][stack]} : {json.loads(resp)['message']}"
|
||||
)
|
||||
return True
|
||||
|
||||
def delete_stack(self, endpoint_id=None, stack=None, timeout=None):
|
||||
@@ -728,13 +767,13 @@ class Portainer:
|
||||
for s in stacks:
|
||||
# print(f"Delete stack {s['Name']}")
|
||||
# print(s['EndpointId'], endpoint_id)
|
||||
if int(s['EndpointId']) != int(endpoint_id):
|
||||
if int(s["EndpointId"]) != int(endpoint_id):
|
||||
continue
|
||||
# print("Deleting stack:", s['Name'])
|
||||
path = f"/stacks/{s['Id']}"
|
||||
if endpoint_id is not None:
|
||||
path += f"?endpointId={endpoint_id}&removeVolumes=true"
|
||||
paths.append([self.get_endpoint_name(endpoint_id), s['Name'], path])
|
||||
paths.append([self.get_endpoint_name(endpoint_id), s["Name"], path])
|
||||
# input(paths)
|
||||
|
||||
def delete(c):
|
||||
@@ -779,8 +818,5 @@ class Portainer:
|
||||
endpoint_id = int(self.endpoints["by_name"][endpoint_id])
|
||||
path = f"/endpoints/{endpoint_id}/docker/secrets/create"
|
||||
encoded = base64.b64encode(value.encode()).decode()
|
||||
data = {
|
||||
"Name": name,
|
||||
"Data": encoded
|
||||
}
|
||||
data = {"Name": name, "Data": encoded}
|
||||
self.api_post(path, data, timeout=timeout)
|
||||
|
||||
Reference in New Issue
Block a user