@@ -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