Skip to content

Commit

Permalink
adding "--v3 list apps|backups|buckets|credentials|hooks|protections|…
Browse files Browse the repository at this point in the history
…snapshots"

Signed-off-by: Michael Haigh <[email protected]>
  • Loading branch information
MichaelHaigh committed Feb 2, 2024
1 parent c73b573 commit df2991f
Show file tree
Hide file tree
Showing 6 changed files with 315 additions and 87 deletions.
40 changes: 32 additions & 8 deletions astraSDK/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,21 +109,45 @@ def printError(self, ret):
except AttributeError:
sys.stderr.write(f"{RED}{ret}{ENDC}")

def recursiveGet(self, k, item):
def recursiveGet(self, k, item, conCatList=[]):
"""Recursion function which is just a wrapper around dict.get(key), to handle cases
where there's a dict within a dict. A '.' in the key name ('metadata.creationTimestamp')
is used for identification purposes."""
if len(k.split(".")) > 1:
return self.recursiveGet(k.split(".", 1)[1], item[k.split(".")[0]])
else:
return item.get(k)
where there's a dict or list within a dict:
- '.' in the key name ('metadata.creationTimestamp') is used to identify a dict
- '[]' in the key name ('spec.includedNamespaces[]') is used to identify a list
- '*' as a key represents a wildcard (returns first entry)
- 'KEYS' represents returning the keys rather than the vaules."""
if len(k.split(".")) > 1 and k.split(".")[0] == "":
return self.recursiveGet(k.split(".", 1)[1], item, conCatList)
elif (len(k.split(".")) > 1 and len(k.split("[]")) == 1) or (
len(k.split(".")[0]) < len(k.split("[]")[0])
):
if k.split(".")[0] == "*":
return self.recursiveGet(k.split(".", 1)[1], item[next(iter(item))], conCatList)
elif k.split(".")[0] == "KEYS":
print("hit the KEYS section")
return self.recursiveGet(
k.split(".", 1)[1], item[k.split(".")[0]].keys(), conCatList
)
try:
return self.recursiveGet(k.split(".", 1)[1], item[k.split(".")[0]], conCatList)
except KeyError:
return "None"
elif (len(k.split("[]")) > 1 and len(k.split(".")) == 1) or (
len(k.split("[]")[0]) < len(k.split(".")[0])
):
for i in item[k.split("[]")[0]]:
conCatList.append(self.recursiveGet(k.split("[]", 1)[1], i, []))
return conCatList
if k == "KEYS":
return list(item.keys())
return item.get(k)

def basicTable(self, tabHeader, tabKeys, dataDict):
"""Function to create a basic tabulate table for terminal printing"""
tabData = []
for item in dataDict["items"]:
# Generate a table row based on the keys list
row = [self.recursiveGet(k, item) for k in tabKeys]
row = [self.recursiveGet(k, item, []) for k in tabKeys]
# Handle cases where table row has a nested list
for c, r in enumerate(row):
if type(r) is list:
Expand Down
133 changes: 125 additions & 8 deletions astraSDK/k8s.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ class getResources(KubeCommon):

def __init__(self, quiet=True, output="json", config_context=None):
"""quiet: Will there be CLI output or just return (datastructure)
output: json: (default) output in JSON
output: table: pretty print the data
json: (default) output in JSON
yaml: output in yaml
config_context: the kubeconfig:context mapping to execute against
None: use system defaults
Expand All @@ -45,9 +46,13 @@ def main(
namespace="astra-connector",
version="v1",
group="astra.netapp.io",
keyFilter=None,
valFilter=None,
filters=[],
):
"""filters must be of format (logical AND if specifying multiple filters, set
inMatch to True to use "in" comparison instead of "=="): [
{"keyFilter": "keyname1", "valFilter": "value1", inMatch=True},
{"keyFilter": "keyname2", "valFilter": "value2"}
]"""
api_instance = kubernetes.client.CustomObjectsApi(self.api_client)
try:
resp = api_instance.list_namespaced_custom_object(
Expand All @@ -56,14 +61,23 @@ def main(
namespace=namespace,
plural=plural,
)
if keyFilter and valFilter:
for f in filters:
filterCopy = copy.deepcopy(resp)
for counter, r in enumerate(filterCopy.get("items")):
if self.recursiveGet(keyFilter, r) != valFilter:
resp["items"].remove(filterCopy["items"][counter])
if f["keyFilter"] and f["valFilter"]:
for counter, r in enumerate(filterCopy.get("items")):
if f.get("inMatch"):
if f["valFilter"] not in self.recursiveGet(f["keyFilter"], r, []):
resp["items"].remove(filterCopy["items"][counter])
else:
if self.recursiveGet(f["keyFilter"], r, []) != f["valFilter"]:
resp["items"].remove(filterCopy["items"][counter])

if self.output == "yaml":
resp = yaml.dump(resp)
elif self.output == "table":
resp = self.basicTable(
self.getTableInfo(plural, headers=True), self.getTableInfo(plural), resp
)

if not self.quiet:
print(json.dumps(resp) if type(resp) is dict else resp)
Expand All @@ -72,6 +86,102 @@ def main(
except kubernetes.client.rest.ApiException as e:
self.printError(e)

def getTableInfo(self, plural, headers=False):
if plural == "applications":
if headers:
return ["name", "namespaces", "labelSelectors", "state"]
return [
"metadata.name",
"spec.includedNamespaces[].namespace",
"spec.includedNamespaces[].labelSelector.matchLabels.app",
"status.conditions[].type",
]
elif plural == "appvaults":
if headers:
return ["name", "credential", "provider", "state"]
return [
"metadata.name",
"spec.providerCredentials.*.valueFromSecret.name",
"spec.providerType",
"status.state",
]
elif plural == "backups":
if headers:
return [
"applicationRef",
"backupName",
"appVaultRef",
"backupState",
"creationTimestamp",
]
return [
"spec.applicationRef",
"metadata.name",
"spec.appVaultRef",
"status.state",
"metadata.creationTimestamp",
]
elif plural == "exechooks":
if headers:
return [
"applicationRef",
"name",
"stage",
"action",
"arguments",
"matchingCriteriaType",
"matchingCriteriaValue",
]
return [
"spec.applicationRef",
"metadata.name",
"spec.stage",
"spec.action",
"spec.arguments",
"spec.matchingCriteria[].type",
"spec.matchingCriteria[].value",
]
elif plural == "schedules":
if headers:
return [
"applicationRef",
"name",
"granularity",
"minute",
"hour",
"dayOfWeek",
"dayOfMonth",
"snapRetention",
"backupRetention",
]
return [
"spec.applicationRef",
"metadata.name",
"spec.granularity",
"spec.minute",
"spec.hour",
"spec.dayOfWeek",
"spec.dayOfMonth",
"spec.snapshotRetention",
"spec.backupRetention",
]
elif plural == "snapshots":
if headers:
return [
"applicationRef",
"snapshotName",
"appVaultRef",
"snapshotState",
"creationTimestamp",
]
return [
"spec.applicationRef",
"metadata.name",
"spec.appVaultRef",
"status.state",
"metadata.creationTimestamp",
]


class getClusterResources(KubeCommon):
"""Get all cluster scoped resources of a specific CRD"""
Expand Down Expand Up @@ -285,7 +395,8 @@ class getSecrets(KubeCommon):

def __init__(self, quiet=True, output="json", config_context=None):
"""quiet: Will there be CLI output or just return (datastructure)
output: json: (default) output in JSON
output: table: pretty print the data
json: (default) output in JSON
yaml: output in yaml
config_context: the kubeconfig:context mapping to execute against
None: use system defaults
Expand All @@ -302,6 +413,12 @@ def main(self, namespace="astra-connector"):

if self.output == "yaml":
resp = yaml.dump(resp)
elif self.output == "table":
resp = self.basicTable(
["name", "type", "dataKeys", "creationTimestamp"],
["metadata.name", "type", "data.KEYS", "metadata.creation_timestamp"],
resp,
)

if not self.quiet:
print(json.dumps(resp, default=str) if type(resp) is dict else resp)
Expand Down
8 changes: 4 additions & 4 deletions tkSrc/choices.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,16 +130,16 @@ def main(argv, verbs, verbPosition, ard, acl, v3):
if v3:
ard.snapshots = astraSDK.k8s.getResources(config_context=v3).main(
"snapshots",
keyFilter="spec.applicationRef",
valFilter=a,
filters=[{"keyFilter": "spec.applicationRef", "valFilter": a}],
)
acl.snapshots = ard.buildList("snapshots", "metadata.name")
else:
ard.snapshots = astraSDK.snapshots.getSnaps().main(appFilter=a)
acl.snapshots = ard.buildList("snapshots", "id")
if argv[verbPosition + 1] == "hook":
ard.scripts = astraSDK.scripts.getScripts().main()
acl.scripts = ard.buildList("scripts", "id")
if not v3:
ard.scripts = astraSDK.scripts.getScripts().main()
acl.scripts = ard.buildList("scripts", "id")
if argv[verbPosition + 1] == "protection":
if v3:
ard.buckets = astraSDK.k8s.getResources(config_context=v3).main("appvaults")
Expand Down
Loading

0 comments on commit df2991f

Please sign in to comment.