Skip to content

Commit 88b49a7

Browse files
muntzerrDavide Schiera
authored andcommitted
Add support for anchore scanning repos (#104)
1 parent bb5cd20 commit 88b49a7

File tree

1 file changed

+144
-10
lines changed

1 file changed

+144
-10
lines changed

sdcclient/_scanning.py

Lines changed: 144 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,81 @@ def _get_registry_type(self, registry):
528528
def _registry_string_is_valid(self, registry):
529529
return re.match(".*\\/.*", registry)
530530

531+
def add_repo(self, repo, autosubscribe=True, lookuptag=None):
532+
'''**Description**
533+
Add a repository
534+
535+
**Arguments**
536+
- repo: Input repository can be in the following formats: registry/repo
537+
- autosubscribe: If unset, instruct the engine to disable subscriptions for any discovered tags.
538+
- lookuptag: Specify a tag to use for repo tag scan if 'latest' tag does not exist in the repo.
539+
540+
**Success Return Value**
541+
A JSON object representing the repo.
542+
'''
543+
url = "{base_url}/api/scanning/v1/anchore/repositories?repository={repo}&autosubscribe={autosubscribe}{lookuptag}".format(
544+
base_url=self.url,
545+
repo=repo,
546+
autosubscribe=autosubscribe,
547+
lookuptag="&lookuptag={}".format(lookuptag) if lookuptag else "")
548+
549+
res = requests.post(url, headers=self.hdrs, verify=self.ssl_verify)
550+
if not self._checkResponse(res):
551+
return [False, self.lasterr]
552+
553+
return [True, res.json()]
554+
555+
def watch_repo(self, repo):
556+
'''**Description**
557+
Instruct engine to start automatically watching the repo for image updates
558+
559+
**Arguments**
560+
- repo: Input repository can be in the following formats: registry/repo
561+
'''
562+
return self.activate_subscription('repo_update', repo)
563+
564+
def unwatch_repo(self, repo):
565+
'''**Description**
566+
Instruct engine to stop automatically watching the repo for image updates
567+
568+
**Arguments**
569+
- repo: Input repository can be in the following formats: registry/repo
570+
'''
571+
return self.deactivate_subscription('repo_update', repo)
572+
573+
def delete_repo(self, repo):
574+
'''**Description**
575+
Delete a repository from the watch list (does not delete already analyzed images)
576+
577+
**Arguments**
578+
- repo: Input repository can be in the following formats: registry/repo
579+
'''
580+
return self.delete_subscription('repo_update', repo)
581+
582+
def list_repos(self):
583+
'''**Description**
584+
List added repositories
585+
586+
**Arguments**
587+
- None
588+
589+
**Success Return Value**
590+
A JSON object representing the list of repositories.
591+
'''
592+
return self.get_subscriptions("repo_update")
593+
594+
def get_repo(self, repo):
595+
'''**Description**
596+
Get a repository
597+
598+
**Arguments**
599+
- repo: Input repository can be in the following formats: registry/repo
600+
601+
**Success Return Value**
602+
A JSON object representing the registry.
603+
'''
604+
return self.get_subscriptions("repo_update", repo)
605+
531606
def add_policy(self, name, rules, comment="", bundleid=None):
532607
'''**Description**
533608
Create a new policy
@@ -753,6 +828,31 @@ def delete_alert(self, policyid):
753828

754829
return [True, res.text]
755830

831+
def get_subscriptions(self, subscription_type=None, subscription_key=None):
832+
'''**Description**
833+
Get the list of subscriptions
834+
835+
**Arguments**
836+
- subscription_type: Type of subscription. Valid options:
837+
- 'tag_update': Receive notification when new image is pushed
838+
- 'policy_eval': Receive notification when image policy status changes
839+
- 'vuln_update': Receive notification when vulnerabilities are added, removed or modified
840+
- 'repo_update': Receive notification when a repo is updated
841+
- subscription_key: Fully qualified name of tag to subscribe to. Eg. docker.io/library/alpine:latest
842+
'''
843+
url = self.url + "/api/scanning/v1/anchore/subscriptions/"
844+
if subscription_key or subscription_type:
845+
url += "?"
846+
if subscription_key:
847+
url += "subscription_key={}&".format(subscription_key)
848+
if subscription_type:
849+
url += "subscription_type={}".format(subscription_type)
850+
res = requests.get(url, headers=self.hdrs, verify=self.ssl_verify)
851+
if not self._checkResponse(res):
852+
return [False, self.lasterr]
853+
854+
return [True, res.json()]
855+
756856
def activate_subscription(self, subscription_type, subscription_key):
757857
'''**Description**
758858
Activate a subscription
@@ -762,6 +862,7 @@ def activate_subscription(self, subscription_type, subscription_key):
762862
- 'tag_update': Receive notification when new image is pushed
763863
- 'policy_eval': Receive notification when image policy status changes
764864
- 'vuln_update': Receive notification when vulnerabilities are added, removed or modified
865+
- 'repo_update': Receive notification when a repo is updated
765866
- subscription_key: Fully qualified name of tag to subscribe to. Eg. docker.io/library/alpine:latest
766867
'''
767868
return self._update_subscription(subscription_type, subscription_key, True)
@@ -775,22 +876,60 @@ def deactivate_subscription(self, subscription_type, subscription_key):
775876
- 'tag_update': Receive notification when new image is pushed
776877
- 'policy_eval': Receive notification when image policy status changes
777878
- 'vuln_update': Receive notification when vulnerabilities are added, removed or modified
879+
- 'repo_update': Receive notification when a repo is updated
778880
- subscription_key: Fully qualified name of tag to subscribe to. Eg. docker.io/library/alpine:latest
779881
'''
780882
return self._update_subscription(subscription_type, subscription_key, False)
781883

884+
def delete_subscription(self, subscription_type, subscription_key):
885+
'''**Description**
886+
Delete a subscription
887+
888+
**Arguments**
889+
- subscription_type: Type of subscription. Valid options:
890+
- 'tag_update': Receive notification when new image is pushed
891+
- 'policy_eval': Receive notification when image policy status changes
892+
- 'vuln_update': Receive notification when vulnerabilities are added, removed or modified
893+
- 'repo_update': Receive notification when a repo is updated
894+
- subscription_key: Fully qualified name of tag to subscribe to. Eg. docker.io/library/alpine:latest
895+
'''
896+
try:
897+
url = self._subscription_url(subscription_type, subscription_key)
898+
except Exception as err:
899+
return [False, err]
900+
901+
res = requests.delete(url, headers=self.hdrs, verify=self.ssl_verify)
902+
if not self._checkResponse(res):
903+
return [False, self.lasterr]
904+
905+
return [True, res.json()]
906+
782907
def _update_subscription(self, subscription_type, subscription_key, activate):
783-
hashstr = '+'.join([self.token, subscription_key, subscription_type]).encode('utf-8')
784-
subscription_id = hashlib.md5(hashstr).hexdigest()
785-
url = self.url + "/api/scanning/v1/anchore/subscriptions/" + subscription_id
786-
payload = {'active': activate, 'subscription_key': subscription_key, 'subscription_type': subscription_type}
908+
try:
909+
url = self._subscription_url(subscription_type, subscription_key)
910+
except Exception as err:
911+
return [False, err]
787912

913+
payload = {'active': activate, 'subscription_key': subscription_key, 'subscription_type': subscription_type}
788914
res = requests.put(url, data=json.dumps(payload), headers=self.hdrs, verify=self.ssl_verify)
789915
if not self._checkResponse(res):
790916
return [False, self.lasterr]
791917

792918
return [True, res.json()]
793919

920+
def _subscription_url(self, subscription_type, subscription_key):
921+
ok, res = self.get_subscriptions(subscription_type, subscription_key)
922+
if not ok:
923+
raise Exception(res)
924+
925+
if len(res) != 1:
926+
raise Exception("Subscription {} doesn't exist".format(subscription_key))
927+
id = res[0].get("subscription_id", None)
928+
if not id:
929+
raise Exception("Subscription malformed")
930+
931+
return self.url + "/api/scanning/v1/anchore/subscriptions/" + id
932+
794933
def list_subscription(self):
795934
'''**Description**
796935
List all subscriptions
@@ -801,12 +940,7 @@ def list_subscription(self):
801940
**Success Return Value**
802941
A JSON object representing the list of subscriptions.
803942
'''
804-
url = self.url + "/api/scanning/v1/anchore/subscriptions"
805-
res = requests.get(url, headers=self.hdrs, verify=self.ssl_verify)
806-
if not self._checkResponse(res):
807-
return [False, self.lasterr]
808-
809-
return [True, res.json()]
943+
return self.get_subscriptions()
810944

811945
def list_runtime(self, scope="", skip_policy_evaluation=True, start_time=None, end_time=None):
812946
'''**Description**

0 commit comments

Comments
 (0)