From c2584fcb054e686bff54c3f69b99500461a2516a Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Thu, 29 Apr 2021 14:39:49 -0700 Subject: [PATCH 001/110] Refactoring proxy lifetime to only shutdown when proxy is out-of-date. --- src/api-service/__app__/onefuzzlib/proxy.py | 36 +++++++++++++++++-- .../__app__/timer_daily/__init__.py | 2 +- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 5d062454e6..4fce19ae1f 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -154,12 +154,30 @@ def stopped(self) -> None: logging.info(PROXY_LOG_PREFIX + "removing proxy: %s", self.region) self.delete() - def is_used(self) -> bool: - if len(self.get_forwards()) == 0: - logging.info(PROXY_LOG_PREFIX + "no forwards: %s", self.region) + def is_outdated(self) -> bool: + if self.version != __version__: + logging.info( + PROXY_LOG_PREFIX + "mismatch version: proxy:%s service:%s state:%s", + self.version, + __version__, + self.state, + ) + return False + if self.timestamp < (datetime.datetime.now() - datatime.timedelta(7)): + logging.info( + PROXY_LOG_PREFIX + "proxy older than 7 days: proxy-created:%s state:%s", + self.timestamp, + self.state + ) return False return True + def is_used(self) -> bool: + if len(self.get_forwards()) == 0: + logging.info(PROXY_LOG_PREFIX + "no forwards: %s", self.region) + return False + return True + def is_alive(self) -> bool: # Unfortunately, with and without TZ information is required for compare # or exceptions are generated @@ -243,6 +261,7 @@ def search_states(cls, *, states: Optional[List[VmState]] = None) -> List["Proxy return cls.search(query=query) @classmethod + # Question for Brian - Why does this not include is_used to check forwards? def get_or_create(cls, region: Region) -> Optional["Proxy"]: proxy = Proxy.get(region) if proxy is not None: @@ -258,6 +277,17 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: proxy.state = VmState.stopping proxy.save() return None + if proxy.timestamp < (datetime.datetime.now() - datatime.timedelta(7)): + logging.info( + PROXY_LOG_PREFIX + "proxy older than 7 days: proxy-created:%s state:%s", + proxy.timestamp, + proxy.state + ) + if proxy.state != VmState.stopping: + # If the proxy is out-of-date, delete and re-create it + proxy.state = VmState.stopping + proxy.save() + return None return proxy logging.info(PROXY_LOG_PREFIX + "creating proxy: region:%s", region) diff --git a/src/api-service/__app__/timer_daily/__init__.py b/src/api-service/__app__/timer_daily/__init__.py index ca6fe1d275..d294829ba1 100644 --- a/src/api-service/__app__/timer_daily/__init__.py +++ b/src/api-service/__app__/timer_daily/__init__.py @@ -16,7 +16,7 @@ def main(mytimer: func.TimerRequest, dashboard: func.Out[str]) -> None: # noqa: F841 for proxy in Proxy.search(): - if not proxy.is_used(): + if not proxy.is_used and not proxy.is_outdated(): logging.info("stopping proxy") proxy.state = VmState.stopping proxy.save() From bdc93abe470c1c3948b1c8032868b5b27ddb2f98 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Thu, 29 Apr 2021 15:09:55 -0700 Subject: [PATCH 002/110] Running isort and black for formatting. Fixing typo. --- src/api-service/__app__/onefuzzlib/proxy.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 4fce19ae1f..dd44262c0d 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -163,20 +163,20 @@ def is_outdated(self) -> bool: self.state, ) return False - if self.timestamp < (datetime.datetime.now() - datatime.timedelta(7)): + if self.timestamp < (datetime.datetime.now() - datetime.timedelta(7)): logging.info( PROXY_LOG_PREFIX + "proxy older than 7 days: proxy-created:%s state:%s", self.timestamp, - self.state + self.state, ) return False return True def is_used(self) -> bool: - if len(self.get_forwards()) == 0: - logging.info(PROXY_LOG_PREFIX + "no forwards: %s", self.region) - return False - return True + if len(self.get_forwards()) == 0: + logging.info(PROXY_LOG_PREFIX + "no forwards: %s", self.region) + return False + return True def is_alive(self) -> bool: # Unfortunately, with and without TZ information is required for compare @@ -261,7 +261,7 @@ def search_states(cls, *, states: Optional[List[VmState]] = None) -> List["Proxy return cls.search(query=query) @classmethod - # Question for Brian - Why does this not include is_used to check forwards? + # Question for Brian - Why does this not include is_used to check forwards? def get_or_create(cls, region: Region) -> Optional["Proxy"]: proxy = Proxy.get(region) if proxy is not None: @@ -277,11 +277,12 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: proxy.state = VmState.stopping proxy.save() return None - if proxy.timestamp < (datetime.datetime.now() - datatime.timedelta(7)): + if proxy.timestamp < (datetime.datetime.now() - datetime.timedelta(7)): logging.info( - PROXY_LOG_PREFIX + "proxy older than 7 days: proxy-created:%s state:%s", + PROXY_LOG_PREFIX + + "proxy older than 7 days: proxy-created:%s state:%s", proxy.timestamp, - proxy.state + proxy.state, ) if proxy.state != VmState.stopping: # If the proxy is out-of-date, delete and re-create it From b3602784fe36219028d48c6ac4acdeef74d60d4b Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Thu, 29 Apr 2021 15:50:26 -0700 Subject: [PATCH 003/110] Editing based of optional timestamp for proxy. --- src/api-service/__app__/onefuzzlib/proxy.py | 48 +++++++++++++-------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index dd44262c0d..2dc5eb1f4e 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -163,13 +163,20 @@ def is_outdated(self) -> bool: self.state, ) return False - if self.timestamp < (datetime.datetime.now() - datetime.timedelta(7)): - logging.info( - PROXY_LOG_PREFIX + "proxy older than 7 days: proxy-created:%s state:%s", - self.timestamp, - self.state, - ) - return False + if self.timestamp is not None: + proxy_timestamp = self.timestamp + if proxy_timestamp < (datetime.datetime.now() - datetime.timedelta(7)): + logging.info( + PROXY_LOG_PREFIX + + "proxy older than 7 days: proxy-created:%s state:%s", + self.timestamp, + self.state, + ) + if self.state != VmState.stopping: + # If the proxy is out-of-date, delete and re-create it + self.state = VmState.stopping + self.save() + return False return True def is_used(self) -> bool: @@ -264,6 +271,7 @@ def search_states(cls, *, states: Optional[List[VmState]] = None) -> List["Proxy # Question for Brian - Why does this not include is_used to check forwards? def get_or_create(cls, region: Region) -> Optional["Proxy"]: proxy = Proxy.get(region) + proxy_timestamp = None if proxy is not None: if proxy.version != __version__: logging.info( @@ -277,18 +285,20 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: proxy.state = VmState.stopping proxy.save() return None - if proxy.timestamp < (datetime.datetime.now() - datetime.timedelta(7)): - logging.info( - PROXY_LOG_PREFIX - + "proxy older than 7 days: proxy-created:%s state:%s", - proxy.timestamp, - proxy.state, - ) - if proxy.state != VmState.stopping: - # If the proxy is out-of-date, delete and re-create it - proxy.state = VmState.stopping - proxy.save() - return None + if proxy.timestamp is not None: + proxy_timestamp = proxy.timestamp + if proxy_timestamp < (datetime.datetime.now() - datetime.timedelta(7)): + logging.info( + PROXY_LOG_PREFIX + + "proxy older than 7 days: proxy-created:%s state:%s", + proxy.timestamp, + proxy.state, + ) + if proxy.state != VmState.stopping: + # If the proxy is out-of-date, delete and re-create it + proxy.state = VmState.stopping + proxy.save() + return None return proxy logging.info(PROXY_LOG_PREFIX + "creating proxy: region:%s", region) From f6fa247dfb8e729e3bf1bf80ad4d64df7d34c889 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Thu, 29 Apr 2021 15:52:00 -0700 Subject: [PATCH 004/110] isort and black. --- src/api-service/__app__/onefuzzlib/proxy.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 2dc5eb1f4e..52d0a13900 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -163,8 +163,8 @@ def is_outdated(self) -> bool: self.state, ) return False - if self.timestamp is not None: - proxy_timestamp = self.timestamp + if self.timestamp is not None: + proxy_timestamp = self.timestamp if proxy_timestamp < (datetime.datetime.now() - datetime.timedelta(7)): logging.info( PROXY_LOG_PREFIX @@ -285,8 +285,8 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: proxy.state = VmState.stopping proxy.save() return None - if proxy.timestamp is not None: - proxy_timestamp = proxy.timestamp + if proxy.timestamp is not None: + proxy_timestamp = proxy.timestamp if proxy_timestamp < (datetime.datetime.now() - datetime.timedelta(7)): logging.info( PROXY_LOG_PREFIX From b9b257267392fc4bf5d78c8cd099ffbc3fe389c2 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Mon, 3 May 2021 13:32:07 -0700 Subject: [PATCH 005/110] Potential fix for time issues. --- src/api-service/__app__/onefuzzlib/proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 52d0a13900..d4c9599bcf 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -287,7 +287,7 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: return None if proxy.timestamp is not None: proxy_timestamp = proxy.timestamp - if proxy_timestamp < (datetime.datetime.now() - datetime.timedelta(7)): + if proxy_timestamp < (datetime.datetime.utcnow() - datetime.timedelta(7)): logging.info( PROXY_LOG_PREFIX + "proxy older than 7 days: proxy-created:%s state:%s", From e238542800fdd855bac8c3593d06028bedfe5c99 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Mon, 3 May 2021 13:35:16 -0700 Subject: [PATCH 006/110] Forgot other .now() changes. --- src/api-service/__app__/onefuzzlib/proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index d4c9599bcf..2665b55fdc 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -165,7 +165,7 @@ def is_outdated(self) -> bool: return False if self.timestamp is not None: proxy_timestamp = self.timestamp - if proxy_timestamp < (datetime.datetime.now() - datetime.timedelta(7)): + if proxy_timestamp < (datetime.datetime.utcnow() - datetime.timedelta(7)): logging.info( PROXY_LOG_PREFIX + "proxy older than 7 days: proxy-created:%s state:%s", From 8452b810fcb31d5c73052adc88c58f8e2ee93b71 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Mon, 3 May 2021 13:48:44 -0700 Subject: [PATCH 007/110] Running isort and black. --- src/api-service/__app__/onefuzzlib/proxy.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 2665b55fdc..a1aaee981c 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -287,7 +287,9 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: return None if proxy.timestamp is not None: proxy_timestamp = proxy.timestamp - if proxy_timestamp < (datetime.datetime.utcnow() - datetime.timedelta(7)): + if proxy_timestamp < ( + datetime.datetime.utcnow() - datetime.timedelta(7) + ): logging.info( PROXY_LOG_PREFIX + "proxy older than 7 days: proxy-created:%s state:%s", From 509eb2eb04c572a77d6126289d08230d384b7f9e Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Mon, 3 May 2021 14:40:06 -0700 Subject: [PATCH 008/110] Correcting datetime to datetime.aware --- src/api-service/__app__/onefuzzlib/proxy.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index a1aaee981c..f5b86f3960 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -165,7 +165,7 @@ def is_outdated(self) -> bool: return False if self.timestamp is not None: proxy_timestamp = self.timestamp - if proxy_timestamp < (datetime.datetime.utcnow() - datetime.timedelta(7)): + if proxy_timestamp < (datetime.datetime.now(tz=datetime.timezone.utc) - datetime.timedelta(7)): logging.info( PROXY_LOG_PREFIX + "proxy older than 7 days: proxy-created:%s state:%s", @@ -188,7 +188,7 @@ def is_used(self) -> bool: def is_alive(self) -> bool: # Unfortunately, with and without TZ information is required for compare # or exceptions are generated - ten_minutes_ago_no_tz = datetime.datetime.utcnow() - datetime.timedelta( + ten_minutes_ago_no_tz = datetime.datetime.now(tz=datetime.timezone.utc) - datetime.timedelta( minutes=10 ) ten_minutes_ago = ten_minutes_ago_no_tz.astimezone(datetime.timezone.utc) @@ -288,7 +288,7 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: if proxy.timestamp is not None: proxy_timestamp = proxy.timestamp if proxy_timestamp < ( - datetime.datetime.utcnow() - datetime.timedelta(7) + datetime.datetime.now(tz=datetime.timezone.utc) - datetime.timedelta(7) ): logging.info( PROXY_LOG_PREFIX From 4ce51e5251628ae0a5c39c460ba94b7d29efb138 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Mon, 3 May 2021 14:42:14 -0700 Subject: [PATCH 009/110] Changing to 30 min lifetime to test. --- src/api-service/__app__/onefuzzlib/proxy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index f5b86f3960..e68608c88e 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -165,7 +165,7 @@ def is_outdated(self) -> bool: return False if self.timestamp is not None: proxy_timestamp = self.timestamp - if proxy_timestamp < (datetime.datetime.now(tz=datetime.timezone.utc) - datetime.timedelta(7)): + if proxy_timestamp < (datetime.datetime.now(tz=datetime.timezone.utc) - datetime.timedelta(minutes=30)): logging.info( PROXY_LOG_PREFIX + "proxy older than 7 days: proxy-created:%s state:%s", @@ -288,7 +288,7 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: if proxy.timestamp is not None: proxy_timestamp = proxy.timestamp if proxy_timestamp < ( - datetime.datetime.now(tz=datetime.timezone.utc) - datetime.timedelta(7) + datetime.datetime.now(tz=datetime.timezone.utc) - datetime.timedelta(minutes=30) ): logging.info( PROXY_LOG_PREFIX From 8389d35457829534aca4b1282f88f5c95dd307bb Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Mon, 3 May 2021 14:52:04 -0700 Subject: [PATCH 010/110] Isort and black. --- src/api-service/__app__/onefuzzlib/proxy.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index e68608c88e..d96d12d7ae 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -165,7 +165,10 @@ def is_outdated(self) -> bool: return False if self.timestamp is not None: proxy_timestamp = self.timestamp - if proxy_timestamp < (datetime.datetime.now(tz=datetime.timezone.utc) - datetime.timedelta(minutes=30)): + if proxy_timestamp < ( + datetime.datetime.now(tz=datetime.timezone.utc) + - datetime.timedelta(minutes=30) + ): logging.info( PROXY_LOG_PREFIX + "proxy older than 7 days: proxy-created:%s state:%s", @@ -188,9 +191,9 @@ def is_used(self) -> bool: def is_alive(self) -> bool: # Unfortunately, with and without TZ information is required for compare # or exceptions are generated - ten_minutes_ago_no_tz = datetime.datetime.now(tz=datetime.timezone.utc) - datetime.timedelta( - minutes=10 - ) + ten_minutes_ago_no_tz = datetime.datetime.now( + tz=datetime.timezone.utc + ) - datetime.timedelta(minutes=10) ten_minutes_ago = ten_minutes_ago_no_tz.astimezone(datetime.timezone.utc) if ( self.heartbeat is not None @@ -288,7 +291,8 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: if proxy.timestamp is not None: proxy_timestamp = proxy.timestamp if proxy_timestamp < ( - datetime.datetime.now(tz=datetime.timezone.utc) - datetime.timedelta(minutes=30) + datetime.datetime.now(tz=datetime.timezone.utc) + - datetime.timedelta(minutes=30) ): logging.info( PROXY_LOG_PREFIX From 17de463905a44afea88109d651b43f6d6f950dca Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Mon, 3 May 2021 17:03:37 -0700 Subject: [PATCH 011/110] Editing comments. --- src/api-service/__app__/onefuzzlib/proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index d96d12d7ae..6e8eca01bd 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -271,7 +271,7 @@ def search_states(cls, *, states: Optional[List[VmState]] = None) -> List["Proxy return cls.search(query=query) @classmethod - # Question for Brian - Why does this not include is_used to check forwards? + # Question - Why does this not include is_used to check forwards? def get_or_create(cls, region: Region) -> Optional["Proxy"]: proxy = Proxy.get(region) proxy_timestamp = None From aa590f3f46da63840cda35d9526b6f6d13828fcd Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 4 May 2021 13:58:14 -0700 Subject: [PATCH 012/110] Creating new 'created_timestamp' field to track create time. --- src/api-service/__app__/onefuzzlib/proxy.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 6e8eca01bd..2b8371436d 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -43,6 +43,7 @@ # onefuzztypes class Proxy(ORMMixin): timestamp: Optional[datetime.datetime] = Field(alias="Timestamp") + created_timestamp = Optional[datatime.datetime] = Field(alias="Created Timestamp") region: Region state: VmState = Field(default=VmState.init) auth: Authentication = Field(default_factory=build_auth) @@ -163,8 +164,8 @@ def is_outdated(self) -> bool: self.state, ) return False - if self.timestamp is not None: - proxy_timestamp = self.timestamp + if self.created_timestamp is not None: + proxy_timestamp = self.created_timestamp if proxy_timestamp < ( datetime.datetime.now(tz=datetime.timezone.utc) - datetime.timedelta(minutes=30) @@ -172,7 +173,7 @@ def is_outdated(self) -> bool: logging.info( PROXY_LOG_PREFIX + "proxy older than 7 days: proxy-created:%s state:%s", - self.timestamp, + self.created_timestamp, self.state, ) if self.state != VmState.stopping: @@ -288,8 +289,8 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: proxy.state = VmState.stopping proxy.save() return None - if proxy.timestamp is not None: - proxy_timestamp = proxy.timestamp + if proxy.created_timestamp is not None: + proxy_timestamp = proxy.created_timestamp if proxy_timestamp < ( datetime.datetime.now(tz=datetime.timezone.utc) - datetime.timedelta(minutes=30) @@ -297,7 +298,7 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: logging.info( PROXY_LOG_PREFIX + "proxy older than 7 days: proxy-created:%s state:%s", - proxy.timestamp, + proxy.created_timestamp, proxy.state, ) if proxy.state != VmState.stopping: @@ -308,7 +309,10 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: return proxy logging.info(PROXY_LOG_PREFIX + "creating proxy: region:%s", region) - proxy = Proxy(region=region) + proxy = Proxy( + created_timestamp=datetime.datetime.now(tz=datetime.timezone.utc), + region=region, + ) proxy.save() send_event(EventProxyCreated(region=region)) return proxy From 2effb04e039ee1662eb5d1276cca633a282111e1 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 4 May 2021 14:45:34 -0700 Subject: [PATCH 013/110] Changing how created_timestamp is initialized. --- src/api-service/__app__/onefuzzlib/proxy.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 2b8371436d..0bad765227 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -43,7 +43,9 @@ # onefuzztypes class Proxy(ORMMixin): timestamp: Optional[datetime.datetime] = Field(alias="Timestamp") - created_timestamp = Optional[datatime.datetime] = Field(alias="Created Timestamp") + created_timestamp: Optional[datatime.datetime] = Field( + default_factory=datetime.datetime.utcnow() + ) region: Region state: VmState = Field(default=VmState.init) auth: Authentication = Field(default_factory=build_auth) @@ -309,10 +311,7 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: return proxy logging.info(PROXY_LOG_PREFIX + "creating proxy: region:%s", region) - proxy = Proxy( - created_timestamp=datetime.datetime.now(tz=datetime.timezone.utc), - region=region, - ) + proxy = Proxy(region=region) proxy.save() send_event(EventProxyCreated(region=region)) return proxy From abc00df1c98cda9d75947cdb2afda3c9d9713db4 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 4 May 2021 15:41:01 -0700 Subject: [PATCH 014/110] Working to make proxy rollover more fluid. --- src/api-service/__app__/onefuzzlib/proxy.py | 10 +++------- src/api-service/__app__/timer_daily/__init__.py | 9 +++++++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 0bad765227..73f489d394 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -165,7 +165,7 @@ def is_outdated(self) -> bool: __version__, self.state, ) - return False + return True if self.created_timestamp is not None: proxy_timestamp = self.created_timestamp if proxy_timestamp < ( @@ -178,12 +178,8 @@ def is_outdated(self) -> bool: self.created_timestamp, self.state, ) - if self.state != VmState.stopping: - # If the proxy is out-of-date, delete and re-create it - self.state = VmState.stopping - self.save() - return False - return True + return True + return False def is_used(self) -> bool: if len(self.get_forwards()) == 0: diff --git a/src/api-service/__app__/timer_daily/__init__.py b/src/api-service/__app__/timer_daily/__init__.py index d294829ba1..0402eb2664 100644 --- a/src/api-service/__app__/timer_daily/__init__.py +++ b/src/api-service/__app__/timer_daily/__init__.py @@ -8,7 +8,7 @@ import azure.functions as func from onefuzztypes.enums import VmState -from ..onefuzzlib.events import get_events +from ..onefuzzlib.events import get_events, send_event from ..onefuzzlib.proxy import Proxy from ..onefuzzlib.webhooks import WebhookMessageLog from ..onefuzzlib.workers.scalesets import Scaleset @@ -16,7 +16,12 @@ def main(mytimer: func.TimerRequest, dashboard: func.Out[str]) -> None: # noqa: F841 for proxy in Proxy.search(): - if not proxy.is_used and not proxy.is_outdated(): + if proxy.is_outdated(): + logging.info("outdated proxy, creating new one.") + new_proxy = Proxy(region=region) + new_proxy.save() + send_event(EventProxyCreated(region=region)) + if not proxy.is_used: logging.info("stopping proxy") proxy.state = VmState.stopping proxy.save() From da279a024c2c7c3db51f6444784df3d91b696d80 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 4 May 2021 15:42:21 -0700 Subject: [PATCH 015/110] Typo. --- src/api-service/__app__/onefuzzlib/proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 73f489d394..8c1eab01e8 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -43,7 +43,7 @@ # onefuzztypes class Proxy(ORMMixin): timestamp: Optional[datetime.datetime] = Field(alias="Timestamp") - created_timestamp: Optional[datatime.datetime] = Field( + created_timestamp: Optional[datetime.datetime] = Field( default_factory=datetime.datetime.utcnow() ) region: Region From fe8af4767c049258afb9b971253faaf759949e4c Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 4 May 2021 16:04:12 -0700 Subject: [PATCH 016/110] Fixing imports. --- src/api-service/__app__/timer_daily/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/api-service/__app__/timer_daily/__init__.py b/src/api-service/__app__/timer_daily/__init__.py index 0402eb2664..09908306b7 100644 --- a/src/api-service/__app__/timer_daily/__init__.py +++ b/src/api-service/__app__/timer_daily/__init__.py @@ -7,6 +7,7 @@ import azure.functions as func from onefuzztypes.enums import VmState +from onefuzztypes.events import EventProxyCreated from ..onefuzzlib.events import get_events, send_event from ..onefuzzlib.proxy import Proxy @@ -18,9 +19,9 @@ def main(mytimer: func.TimerRequest, dashboard: func.Out[str]) -> None: # noqa: for proxy in Proxy.search(): if proxy.is_outdated(): logging.info("outdated proxy, creating new one.") - new_proxy = Proxy(region=region) + new_proxy = Proxy(region=proxy.region) new_proxy.save() - send_event(EventProxyCreated(region=region)) + send_event(EventProxyCreated(region=proxy.region)) if not proxy.is_used: logging.info("stopping proxy") proxy.state = VmState.stopping From c1ab4bb21ed96de52c0f099f91a71ce5c63cb111 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 4 May 2021 16:16:05 -0700 Subject: [PATCH 017/110] Removing trailing white space. --- src/api-service/__app__/timer_daily/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/timer_daily/__init__.py b/src/api-service/__app__/timer_daily/__init__.py index 09908306b7..57f6da751c 100644 --- a/src/api-service/__app__/timer_daily/__init__.py +++ b/src/api-service/__app__/timer_daily/__init__.py @@ -17,7 +17,7 @@ def main(mytimer: func.TimerRequest, dashboard: func.Out[str]) -> None: # noqa: F841 for proxy in Proxy.search(): - if proxy.is_outdated(): + if proxy.is_outdated(): logging.info("outdated proxy, creating new one.") new_proxy = Proxy(region=proxy.region) new_proxy.save() From 9913bc19747bafd860f144dd0610da9923e4cda8 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 4 May 2021 16:28:42 -0700 Subject: [PATCH 018/110] Resolving type error. --- src/api-service/__app__/onefuzzlib/proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 8c1eab01e8..3781c39774 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -43,7 +43,7 @@ # onefuzztypes class Proxy(ORMMixin): timestamp: Optional[datetime.datetime] = Field(alias="Timestamp") - created_timestamp: Optional[datetime.datetime] = Field( + created_timestamp: datetime.datetime = Field( default_factory=datetime.datetime.utcnow() ) region: Region From c635bd7f979a4fe52bacb9909c81eba3caa920db Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 4 May 2021 16:36:34 -0700 Subject: [PATCH 019/110] Type issues (cont) --- src/api-service/__app__/onefuzzlib/proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 3781c39774..afd238e403 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -44,7 +44,7 @@ class Proxy(ORMMixin): timestamp: Optional[datetime.datetime] = Field(alias="Timestamp") created_timestamp: datetime.datetime = Field( - default_factory=datetime.datetime.utcnow() + default_factory=datetime.datetime.utcnow ) region: Region state: VmState = Field(default=VmState.init) From 349cd80fe1c5135491840149a19b81163c134b20 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Wed, 5 May 2021 10:59:45 -0700 Subject: [PATCH 020/110] Fixing change in proxy.py is_alive() --- src/api-service/__app__/onefuzzlib/proxy.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index afd238e403..e4ff36f6ec 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -190,9 +190,9 @@ def is_used(self) -> bool: def is_alive(self) -> bool: # Unfortunately, with and without TZ information is required for compare # or exceptions are generated - ten_minutes_ago_no_tz = datetime.datetime.now( - tz=datetime.timezone.utc - ) - datetime.timedelta(minutes=10) + ten_minutes_ago_no_tz = datetime.datetime.utcnow() - datetime.timedelta( + minutes=10 + ) ten_minutes_ago = ten_minutes_ago_no_tz.astimezone(datetime.timezone.utc) if ( self.heartbeat is not None From 41ee0f9265418b9daf4dfaf6bcfb5bdb9de2f55e Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Thu, 6 May 2021 11:47:25 -0700 Subject: [PATCH 021/110] Updating proxy with new identifying field. Updated save config with new id. --- src/api-service/__app__/onefuzzlib/proxy.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index e4ff36f6ec..ff990cc93e 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -46,6 +46,7 @@ class Proxy(ORMMixin): created_timestamp: datetime.datetime = Field( default_factory=datetime.datetime.utcnow ) + proxy_id: UUID = Field(default_factory=uuid4) region: Region state: VmState = Field(default=VmState.init) auth: Authentication = Field(default_factory=build_auth) @@ -53,10 +54,11 @@ class Proxy(ORMMixin): error: Optional[Error] version: str = Field(default=__version__) heartbeat: Optional[ProxyHeartbeat] + outdated: bool = Field(default=False) @classmethod def key_fields(cls) -> Tuple[str, Optional[str]]: - return ("region", None) + return ("region", "proxy_id") def get_vm(self) -> VM: vm = VM( @@ -239,7 +241,7 @@ def save_proxy_config(self) -> None: proxy_config = ProxyConfig( url=get_file_sas_url( Container("proxy-configs"), - "%s/config.json" % self.region, + "%s/%s/config.json" % (self.region, self.proxy_id), StorageType.config, read=True, ), @@ -257,7 +259,7 @@ def save_proxy_config(self) -> None: save_blob( Container("proxy-configs"), - "%s/config.json" % self.region, + "%s/%s/config.json" % (self.region, self.proxy_id), proxy_config.json(), StorageType.config, ) From fb573a83799fa1441c975d7bb8f24ec99478e958 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Thu, 6 May 2021 12:28:56 -0700 Subject: [PATCH 022/110] Proxy searches on outdated field and new id. --- src/api-service/__app__/onefuzzlib/proxy.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index ff990cc93e..f6e7ad5827 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -7,6 +7,7 @@ import logging import os from typing import List, Optional, Tuple +from uuid import UUID, uuid4 from azure.mgmt.compute.models import VirtualMachine from onefuzztypes.enums import ErrorCode, VmState @@ -62,7 +63,7 @@ def key_fields(cls) -> Tuple[str, Optional[str]]: def get_vm(self) -> VM: vm = VM( - name="proxy-%s" % self.region, + name="proxy-%s-%s" % (self.region, self.proxy_id), region=self.region, sku=PROXY_SKU, image=PROXY_IMAGE, @@ -167,6 +168,7 @@ def is_outdated(self) -> bool: __version__, self.state, ) + self.outdated = True return True if self.created_timestamp is not None: proxy_timestamp = self.created_timestamp @@ -180,6 +182,7 @@ def is_outdated(self) -> bool: self.created_timestamp, self.state, ) + self.outdated = True return True return False @@ -274,7 +277,7 @@ def search_states(cls, *, states: Optional[List[VmState]] = None) -> List["Proxy @classmethod # Question - Why does this not include is_used to check forwards? def get_or_create(cls, region: Region) -> Optional["Proxy"]: - proxy = Proxy.get(region) + proxy = Proxy.search(query={"region": region, "outdated": False}) proxy_timestamp = None if proxy is not None: if proxy.version != __version__: @@ -288,6 +291,7 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: # If the proxy is out-of-date, delete and re-create it proxy.state = VmState.stopping proxy.save() + proxy.outdated = True return None if proxy.created_timestamp is not None: proxy_timestamp = proxy.created_timestamp @@ -305,6 +309,7 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: # If the proxy is out-of-date, delete and re-create it proxy.state = VmState.stopping proxy.save() + proxy.outdated = True return None return proxy From b011111bbcd2fca49b5a76e9713728212260a281 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Thu, 6 May 2021 14:11:53 -0700 Subject: [PATCH 023/110] Fixing search() call. --- src/api-service/__app__/onefuzzlib/proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index f6e7ad5827..1ac52ca7cd 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -277,7 +277,7 @@ def search_states(cls, *, states: Optional[List[VmState]] = None) -> List["Proxy @classmethod # Question - Why does this not include is_used to check forwards? def get_or_create(cls, region: Region) -> Optional["Proxy"]: - proxy = Proxy.search(query={"region": region, "outdated": False}) + proxy = Proxy.search(query={"region": region, "outdated": [False]}) proxy_timestamp = None if proxy is not None: if proxy.version != __version__: From a17026a635938a30f01d95a6d7714a4df0059e09 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Thu, 6 May 2021 14:16:09 -0700 Subject: [PATCH 024/110] Specifying number of search results. --- src/api-service/__app__/onefuzzlib/proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 1ac52ca7cd..41b0a447d3 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -277,7 +277,7 @@ def search_states(cls, *, states: Optional[List[VmState]] = None) -> List["Proxy @classmethod # Question - Why does this not include is_used to check forwards? def get_or_create(cls, region: Region) -> Optional["Proxy"]: - proxy = Proxy.search(query={"region": region, "outdated": [False]}) + proxy = Proxy.search(query={"region": region, "outdated": [False]}, num_results=1) proxy_timestamp = None if proxy is not None: if proxy.version != __version__: From 62ad0ff2e3149418415c33d30f539c52ab40c7cb Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Thu, 6 May 2021 14:46:46 -0700 Subject: [PATCH 025/110] isort and black --- src/api-service/__app__/onefuzzlib/proxy.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 41b0a447d3..8320a87ad2 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -277,7 +277,9 @@ def search_states(cls, *, states: Optional[List[VmState]] = None) -> List["Proxy @classmethod # Question - Why does this not include is_used to check forwards? def get_or_create(cls, region: Region) -> Optional["Proxy"]: - proxy = Proxy.search(query={"region": region, "outdated": [False]}, num_results=1) + proxy = Proxy.search( + query={"region": region, "outdated": [False]}, num_results=1 + ) proxy_timestamp = None if proxy is not None: if proxy.version != __version__: From c5f98cdcc8ff05de25bf80271ab8a2f3df578c3d Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Thu, 6 May 2021 15:45:03 -0700 Subject: [PATCH 026/110] Fixin list entry. --- src/api-service/__app__/onefuzzlib/proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 8320a87ad2..afd912e216 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -278,7 +278,7 @@ def search_states(cls, *, states: Optional[List[VmState]] = None) -> List["Proxy # Question - Why does this not include is_used to check forwards? def get_or_create(cls, region: Region) -> Optional["Proxy"]: proxy = Proxy.search( - query={"region": region, "outdated": [False]}, num_results=1 + query={"region": [region], "outdated": [False]}, num_results=1 ) proxy_timestamp = None if proxy is not None: From 8f8b75da78460af1b13c89ef1209a6cc9e417a54 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Thu, 6 May 2021 16:09:02 -0700 Subject: [PATCH 027/110] Pulling first entry from proxy list. --- src/api-service/__app__/onefuzzlib/proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index afd912e216..6389c3bc29 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -279,7 +279,7 @@ def search_states(cls, *, states: Optional[List[VmState]] = None) -> List["Proxy def get_or_create(cls, region: Region) -> Optional["Proxy"]: proxy = Proxy.search( query={"region": [region], "outdated": [False]}, num_results=1 - ) + )[0] proxy_timestamp = None if proxy is not None: if proxy.version != __version__: From 07a6c260ba4ac5a135c7ac43d5975ea1019c7168 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Thu, 6 May 2021 16:24:03 -0700 Subject: [PATCH 028/110] isort and black. --- src/api-service/__app__/onefuzzlib/proxy.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 6389c3bc29..9192076e22 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -284,7 +284,8 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: if proxy is not None: if proxy.version != __version__: logging.info( - PROXY_LOG_PREFIX + "mismatch version: proxy:%s service:%s state:%s", + PROXY_LOG_PREFIX + + "mismatch version: proxy:%s service:%s state:%s", proxy.version, __version__, proxy.state, From 5e0d9991b6a74a16c46513f384195fef699b5521 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Fri, 7 May 2021 09:53:02 -0700 Subject: [PATCH 029/110] Index issue. --- src/api-service/__app__/onefuzzlib/proxy.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 9192076e22..a0efc08972 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -277,11 +277,12 @@ def search_states(cls, *, states: Optional[List[VmState]] = None) -> List["Proxy @classmethod # Question - Why does this not include is_used to check forwards? def get_or_create(cls, region: Region) -> Optional["Proxy"]: - proxy = Proxy.search( + proxy_list = Proxy.search( query={"region": [region], "outdated": [False]}, num_results=1 - )[0] + ) proxy_timestamp = None - if proxy is not None: + if len(proxy_list) is not 0: + proxy = proxy_list[0] if proxy.version != __version__: logging.info( PROXY_LOG_PREFIX From 5ed4b4b563add3fa0567d5f7e74b81430e1462b7 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Fri, 7 May 2021 09:59:02 -0700 Subject: [PATCH 030/110] Typo. --- src/api-service/__app__/onefuzzlib/proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index a0efc08972..44e3e512b6 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -281,7 +281,7 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: query={"region": [region], "outdated": [False]}, num_results=1 ) proxy_timestamp = None - if len(proxy_list) is not 0: + if len(proxy_list) == 0: proxy = proxy_list[0] if proxy.version != __version__: logging.info( From d7a602fc726ace696f900a7ba0beb74a35ebbe78 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Fri, 7 May 2021 11:16:42 -0700 Subject: [PATCH 031/110] Typo. --- src/api-service/__app__/onefuzzlib/proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 44e3e512b6..1c60c0fddf 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -281,7 +281,7 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: query={"region": [region], "outdated": [False]}, num_results=1 ) proxy_timestamp = None - if len(proxy_list) == 0: + if len(proxy_list) != 0: proxy = proxy_list[0] if proxy.version != __version__: logging.info( From 7eb4742e0c6da38857a40f6a6129cc08a70a0647 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Fri, 7 May 2021 13:29:05 -0700 Subject: [PATCH 032/110] Fixing extension save. --- src/api-service/__app__/onefuzzlib/extension.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/extension.py b/src/api-service/__app__/onefuzzlib/extension.py index 306af4559b..74461d81e5 100644 --- a/src/api-service/__app__/onefuzzlib/extension.py +++ b/src/api-service/__app__/onefuzzlib/extension.py @@ -345,7 +345,7 @@ def proxy_manager_extensions(region: Region) -> List[Extension]: urls = [ get_file_sas_url( Container("proxy-configs"), - "%s/config.json" % region, + "%s/%s/config.json" % (self.region, self.proxy_id), StorageType.config, read=True, ), From c452b114a0167059e8fe4083944bee5f63684ee9 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Fri, 7 May 2021 13:42:22 -0700 Subject: [PATCH 033/110] Adding proxy_id to event fields. --- src/api-service/__app__/onefuzzlib/extension.py | 4 ++-- src/api-service/__app__/onefuzzlib/proxy.py | 10 ++++++---- src/api-service/__app__/timer_daily/__init__.py | 2 +- src/pytypes/onefuzztypes/events.py | 3 +++ 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/extension.py b/src/api-service/__app__/onefuzzlib/extension.py index 74461d81e5..9a6ec23086 100644 --- a/src/api-service/__app__/onefuzzlib/extension.py +++ b/src/api-service/__app__/onefuzzlib/extension.py @@ -341,11 +341,11 @@ def repro_extensions( return extensions -def proxy_manager_extensions(region: Region) -> List[Extension]: +def proxy_manager_extensions(region: Region, proxy_id: UUID) -> List[Extension]: urls = [ get_file_sas_url( Container("proxy-configs"), - "%s/%s/config.json" % (self.region, self.proxy_id), + "%s/%s/config.json" % (region, proxy_id), StorageType.config, read=True, ), diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 1c60c0fddf..d4e0559179 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -110,7 +110,9 @@ def set_failed(self, error: Error) -> None: return logging.error(PROXY_LOG_PREFIX + "vm failed: %s - %s", self.region, error) - send_event(EventProxyFailed(region=self.region, error=error)) + send_event( + EventProxyFailed(region=self.region, proxy_id=self.proxy_id, error=error) + ) self.error = error self.state = VmState.stopping self.save() @@ -137,7 +139,7 @@ def extensions_launch(self) -> None: return self.ip = ip - extensions = proxy_manager_extensions(self.region) + extensions = proxy_manager_extensions(self.region, self.proxy_id) result = vm.add_extensions(extensions) if isinstance(result, Error): self.set_failed(result) @@ -320,9 +322,9 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: logging.info(PROXY_LOG_PREFIX + "creating proxy: region:%s", region) proxy = Proxy(region=region) proxy.save() - send_event(EventProxyCreated(region=region)) + send_event(EventProxyCreated(region=region, proxy_id=proxy.proxy_id)) return proxy def delete(self) -> None: super().delete() - send_event(EventProxyDeleted(region=self.region)) + send_event(EventProxyDeleted(region=self.region, proxy_id=self.proxy_id)) diff --git a/src/api-service/__app__/timer_daily/__init__.py b/src/api-service/__app__/timer_daily/__init__.py index 57f6da751c..a3bc3ae488 100644 --- a/src/api-service/__app__/timer_daily/__init__.py +++ b/src/api-service/__app__/timer_daily/__init__.py @@ -21,7 +21,7 @@ def main(mytimer: func.TimerRequest, dashboard: func.Out[str]) -> None: # noqa: logging.info("outdated proxy, creating new one.") new_proxy = Proxy(region=proxy.region) new_proxy.save() - send_event(EventProxyCreated(region=proxy.region)) + send_event(EventProxyCreated(region=proxy.region, proxy_id=proxy.proxy_id)) if not proxy.is_used: logging.info("stopping proxy") proxy.state = VmState.stopping diff --git a/src/pytypes/onefuzztypes/events.py b/src/pytypes/onefuzztypes/events.py index 537dcb2bcf..c0e5bc2767 100644 --- a/src/pytypes/onefuzztypes/events.py +++ b/src/pytypes/onefuzztypes/events.py @@ -121,14 +121,17 @@ class EventPoolCreated(BaseEvent): class EventProxyCreated(BaseEvent): region: Region + proxy_id: UUID class EventProxyDeleted(BaseEvent): region: Region + proxy_id: UUID class EventProxyFailed(BaseEvent): region: Region + proxy_id: UUID error: Error From 67b1ec1907d6ea5d6dde5c5093ed5b661328c7ff Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Fri, 7 May 2021 13:50:40 -0700 Subject: [PATCH 034/110] Fixing generate docs. --- src/pytypes/extra/generate-docs.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pytypes/extra/generate-docs.py b/src/pytypes/extra/generate-docs.py index c2c39c4468..5906837936 100755 --- a/src/pytypes/extra/generate-docs.py +++ b/src/pytypes/extra/generate-docs.py @@ -147,10 +147,11 @@ def main() -> None: state=TaskState.init, config=task_config, ), - EventProxyCreated(region=Region("eastus")), - EventProxyDeleted(region=Region("eastus")), + EventProxyCreated(region=Region("eastus"), proxy_id=UUID(int=0)), + EventProxyDeleted(region=Region("eastus"), proxy_id=UUID(int=0)), EventProxyFailed( region=Region("eastus"), + proxy_id=UUID(int=0), error=Error(code=ErrorCode.PROXY_FAILED, errors=["example error message"]), ), EventPoolCreated( From 8c7a127936eb0e6492607a2b066c52fd7a2a6ca0 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Fri, 7 May 2021 14:10:20 -0700 Subject: [PATCH 035/110] Resolving event issues. --- src/api-service/__app__/onefuzzlib/proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index d4e0559179..7213730a54 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -288,7 +288,7 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: if proxy.version != __version__: logging.info( PROXY_LOG_PREFIX - + "mismatch version: proxy:%s service:%s state:%s", + + "mismatch version: proxy :%s service:%s state:%s", proxy.version, __version__, proxy.state, From 41816900a3b2276b925a5a0361a17182dd126a58 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Fri, 7 May 2021 15:10:17 -0700 Subject: [PATCH 036/110] Fixing webhook events. --- docs/webhook_events.md | Bin 165862 -> 345860 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/webhook_events.md b/docs/webhook_events.md index e4c5c1d2fa543921d53e1a6fc97bdd7aac2c6eb1..49b3137d39e4205a0da38f93453f18de7255c4e9 100644 GIT binary patch literal 345860 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 PqIEO=G2kEOg8%+MkE{#r literal 165862 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 I^ytz52UY0d5C8xG From 5789793938e9a04329ea70c7269b07cb7819078f Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Fri, 7 May 2021 18:01:26 -0700 Subject: [PATCH 037/110] Generate Docs. --- src/api-service/__app__/onefuzzlib/proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 7213730a54..2e156941e1 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -319,7 +319,7 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: return None return proxy - logging.info(PROXY_LOG_PREFIX + "creating proxy: region:%s", region) + logging.info(PROXY_LOG_PREFIX + "creating proxy : region:%s", region) proxy = Proxy(region=region) proxy.save() send_event(EventProxyCreated(region=region, proxy_id=proxy.proxy_id)) From f80ab84d4dc8df3f568c0ce7928e3e07ef097edd Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Mon, 10 May 2021 13:25:27 -0700 Subject: [PATCH 038/110] CRLF to LF for webhook_events.py. --- docs/webhook_events.md | Bin 345860 -> 334090 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/webhook_events.md b/docs/webhook_events.md index 49b3137d39e4205a0da38f93453f18de7255c4e9..f126e6f90c37894ea1e96f3f1f8a45040707753f 100644 GIT binary patch literal 334090 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

V>f`);#D|z0k$qaB>w;a*=|!o4;%nMz=anV$H<< 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 literal 345860 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 PqIEO=G2kEOg8%+MkE{#r From a148a952fa700b688fe03097657b42c446f091aa Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Mon, 10 May 2021 15:13:58 -0700 Subject: [PATCH 039/110] Rerunning. --- src/api-service/__app__/onefuzzlib/proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 2e156941e1..5814612003 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -180,7 +180,7 @@ def is_outdated(self) -> bool: ): logging.info( PROXY_LOG_PREFIX - + "proxy older than 7 days: proxy-created:%s state:%s", + + "proxy older than 7 days:proxy-created:%s state:%s", self.created_timestamp, self.state, ) From 6ccb76982327ed09892b03b1e9bd043720609cfa Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 11 May 2021 09:26:28 -0700 Subject: [PATCH 040/110] Native to ascii. --- docs/webhook_events.md | Bin 334090 -> 334088 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/webhook_events.md b/docs/webhook_events.md index f126e6f90c37894ea1e96f3f1f8a45040707753f..ffa1476cd32a9bed895af2c0e9c9a112166caa3d 100644 GIT binary patch delta 27 hcmeC$B+{`-q@jgz3)2SU_D#l2K+L>-lQB!gQvjPC3ikj2 delta 79 zcmV-V0I>gvvJ{H46o7;Qv;wd*w|r>>kOY@-JOT>0j^P9D2)Dim1Gq%9@U~P0v%q|A l2$SIQ7PD@ZJO!7)TmwRvKz#%%hi;exhi;eyw{DmOnt(VOAyohX From 3e93a0bf6ac7663ee944ab4878e674e7ff1ad41a Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 11 May 2021 09:35:27 -0700 Subject: [PATCH 041/110] Type. --- docs/webhook_events.md | Bin 334088 -> 334090 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/webhook_events.md b/docs/webhook_events.md index ffa1476cd32a9bed895af2c0e9c9a112166caa3d..f126e6f90c37894ea1e96f3f1f8a45040707753f 100644 GIT binary patch delta 79 zcmV-V0I>gvvJ{H46o7;Qv;wd*w|r>>kOY@-JOT>0j^P9D2)Dim1Gq%9@U~P0v%q|A l2$SIQ7PD@ZJO!7)TmwRvKz#%%hi;exhi;eyw{DmOnt(VOAyohX delta 27 hcmeC$B+{`-q@jgz3)2SU_D#l2K+L>-lQB!gQvjPC3ikj2 From c368ec9afb38fd12644289be44e0c849d8364b14 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 11 May 2021 13:21:27 -0700 Subject: [PATCH 042/110] Uploading webhook_events.md --- docs/webhook_events.md | Bin 334090 -> 167348 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/webhook_events.md b/docs/webhook_events.md index f126e6f90c37894ea1e96f3f1f8a45040707753f..c7b1a2450bd227e11f477826aa32e54a5813acd2 100644 GIT binary patch literal 167348 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

V>f`);#D|z0k$qaB>w;a*=|!o4;%nMz=anV$H<< zJyA-iQVDO}k=&{m5{iCndCzu5vUU4TeHfq5d+LMAn*ApmDteZ@RWI~;LI1{{7HV--#er|Q0eOHt749SDM-MUQY zlDvuCrd2O&)eCpxwLIMXB3{fRk%89jICeZmvwiiv<4~^c^m7$B<-%%%L!(iKv9&B! zWW%$+5}xt<^7+42FC3HO>EGi{$g;Jmj=xSjk>OKO3D!ZkX$8stNaV@r(;3Q3xp{$- zL+YE#!>V0|xN3dpF!DFb=HRH*Wc2|mY1In}qqI#c1!^|>+b$kfdUjDWI8a07)Qqc* z?Wl$^H5t?y^zxjY_W zt;IV=cf6=tNi{J_2ezpNxRkzY)eGNs^;<{-X|1%Jluhw89E(vOD)gOd4z@ZoA< zN>gKY9uOndF?8#J<44yMLd^U!QjWw%#gn_8PnO??lJCnvVM1r{}pgT9*sI zx!!&eZompZ+4!_jDnt5~6vZN9Fj z{>tashB0yni`agdleRyR6P45rp$54x`%us_ICV0tKkPFs{Z{YJxP?!5ScWZAjQ3V% z_(t@Q*5stL#C=DoMfp-F&+UHc9UNCge&*?q?~&G0IYc^Ri#r2w0`R%)N!RWBjoo35 zf#dJL_B<@O7){_uWY`bQCw_Pw{gfwo@2R!#I59n(Buoqq*I_RwahGwA+;Xmcd2p1J-feQ{mh< zowd8fUPyWDGlpUczoA%Y+(Q{hYKCXO)gJfE&vmgw>L$(f9F{KSYryU>{`)VHM^F{Djx z^D?7mXGi;Vf5nltG8>FCJ|ETLSbaMfS#JvZ>J zSQK$J&!KqA`c5bPDyw)RlHyb%Bd;X0?1}t^D9LGe>pXVIaZ5J#I&;t^>~d9S5Pf+e zBjLU^U>2iTyF2V_Jat%@NvW^|zmCFO?YUvxPL;R1PvLHwZ#TM-7W~%obg-x7c6fM4 z|GIDbvM{Z?vURbMR7zUVSo7^JDKxBoy2kftGg+1e*t-br6)(qoC<)}B&n;@3e@&C0pf_8!JimfY)6 z%404Gpxdg%D6W0(gpCJ^c`p!xrN&z)>S-}gLH91k*>;`~$C*Y)@B)@OecGzTz-vXO z(ZkMi#~IZ30&Sdwhva%LGX4E1nU&HBY&L%^oI-VDZ8wlS9BZw#g3LO^n{iVOxCnci zZ$$(4c~o=o7VhoinBXXmx!v3mXyX26m*-sL#i@jd?Vsi3wcfFSY%uKhHSdRb@LS9K z`Mc1Z%l9Vi)_$v{jqUk7AR8iU_JJ&jG%qO5HL~z}N8}%L3F1lEOP;13mdSr;Rbo_z zmHLi|reV((Ry*!Fa+&M6#~RpvDLbHHJ&$pD1gRC`cFlEMcQ>v-E_Cae%;I(T|LHWh zoK^({UGK0+9$eh-(QXn^dPH0YaN>?dNL6FJ@sy_E^|1etdZx;&WoZZZ%C#ynu@(Y0 z37PHYpCXMQlcr*F8O?*4!76+!{Qp#Ds?~1l;;AW3-@oX&Rf&No4CuBhF}_^2|4w&e zL8CY;1f8DS5sdpQvn2SIDSJlHm)i<59)4CyRuMl9@ zIsr{NE3H+DA)SS7A3ZhSGuHaHtT+Vk6BdAf{?|#j`sm{WiTFMiISpF?-ZpmJ;Vpqy zKFlmzLT&Bwv>3zlMbeHv+F82}2%*p)b_*kd=B z=ccCGh9M0dMWtxC*J zWL&EfGd%{moCHR^6!!!m&m4E3@%F4bnUh8#c}{h^`TnBcX(X44ZZy z%g@p(S+SlW_HmHr#d8fO*_~7?ng-hdCwhsKd_+9fKGS(vwabuiyS{T6`8%-6@N4w} zDrr?>NYiQCs>Gx)@xMvosd3`uWp)(Ebk=nZ)c#{TlQjk+IBn8IZ z5p|=p)i{REozM-}z;EN5M26=!{+1@oOD(_cE5+cD4C`AlWD-1M$SB-5zwb5EGcT$1 zGG^6^`w#*(QX-}ci?=Yh5R&#&@OtN}6z~txB-mBa+x$Z-IE7ct=hiLGe!T#vv#|4V zDq82YPzW;i5Y-KDea2azRyP2=1N%lRAu9Dz#;^^kk>9c{i9PmJK7g%Jm;as8N!Z6_ ztU`TxcRQNJO@tQPbRBUoxs{~9otSaPSQw$w_TQEOCYpl7V&;=JLa$Vu?@G*{kb+SF=i5-2hUOMR)v- zZAfj9UC?5X@Q!|Vo#%pVjrhq}+s(}dt-=7Qoh|Buxyl3%H$P_Tx#&B3gGs9}0H`>- z5BY+y!%%s_dFQnn6MiD&%cbY6LffEJqq*J5@-c94!85tX7B_5+TU*$}xRp9T8vobW z`DqtR_o0^RTvD#Bd-*H$T%1GZd_Kq}STD%>91~T)e=Y&v%K7y8X6Kw$w!VG4>#~A> z3cb$><38x5W7ry8cP&B>-Pts0FN*J}H<-ci3Tpujjmfdu_R(C}YYA8TJu>Ua>CffP z%sIc)=Z$a`v6Ox; zgDT&%WVO31!NGegmY|ExmRiw%&-;)O(93>R$a0y78gd`o*{4=vpj8+khiaRb88!Po z+Nb*~j`)^oE$}pU*22hH+&4dSUo^JR%g1UJ1_(<;d8Yb{^X7tIUIut6{rFlg7w1qI zyT9rztkj3g#c1NOE-Rm1a|O$wskGCt$4|1huVvBDhDl*KWYwTIAHUP{*)2b9D4w#u z2wAHz5OSDC=YU_QiCTq$5Jr@GF-KwN>~e03xh~P}R$+kql@>JCe7mOZ&#|&bc$R!j z%wwJy?V2h45N-(Yvn${U0Mo6)K&vo7Nwwr&PpdFsX>I*3q(VdqTZI9I4s9GeudB{G zO!w)(O|r#GyeiHv^<}Wzk3C7;Q;ixW{XQ4_$VZC!l*_mAZ~bo-1_+zD1+4V8|5jnZ z!KakYtQ*#m>xG*hhrH?(6Cc-pfOYTsx^Ky}v8@@bQ{}4-kv0237DSpC6z8{Dcs&F^ zs8twH8C|LouzS%e3@F_Eze-^U2s$6DRT%K$7yj+`MY&_qDhvQR4kpY~M$Cry=+|3g zA6hK#o^TZ*(<%(S6>7O9)ZuCuO!H%FcMwYDAo|rh3pF|Ip`-v`)a31!=ZeSUl&>tk z4L&|eHXIG$=pS1qm3?dYx_u}6F8x+vz~Tcv!`mthkjKLo=dmovHQzJ#J#ATW2;L`j zU*9R?rW?QyeYr-2TB|UC-Jw=tpqyJMWz)TyS{s)=m05&*dYGaQPjZDisZ|&tuGs?T zzNE*#W2-RWq`ME7R$;(VOe~w~c3o=x;A5dnoC7$%$iW?!q-FAMw`mmyT7`k>+1z^G zaVWky{agjkq2qpuxvIV(8=n15i@z@vpRMmC26zuKhtjih)O&ajw}_T-KB7G?Cl`5bh}eVXO5cPJ z2%FMXcSqEy^xZd|e8T$b=K4-##%uXE_Pv_*5uT?TcUwU&i|!-~vp(Mo7LZS)sSRY? zJbETY?WbWah1=|aZJfw>-QmsM-H$hS8=Mi?$`kH3Eo2Sq-E~(6@H*}mF01~&RlW|}0Q(d%eY!BKaC>eWv zV|&sYGsD}u#`Xs--)d;k=9y;wLaO1ucmMwP{4JY4FW_%WIv;`s?cbR`B>hX)d|P5e zxN!s4Qi%n?KI5o?yD_uo+@G!G*gspBfzq;<2#>zj!AUD#%t0H-aM2#uNgc4JQ1@bv z_1-`4YppAV_uOh|?9Ou;;kUqs$yw;+mdm;?!RdV71zkc{kfY_5TyQI~%J0;Z;QGgV zDy@dbuN&9}R;2<$*VbgtKYA<{GIwBxr4;V$j`PwGqi{D-f9Ca+qb19>K{nH4v3rg? ziW$=O_1e7;26cD-+3PWE-x*Ws%K-(&Vq#zhY>E6A2s-^Ag?im#U3 zZw1~6A!$*ivxi9s_7m`dEV&X&Ynz)9>%iUt_tcaoevmj1B}N?AOZj?C9TXRhYqd`( z|D87(-Ye8`X%1W{jC zp#k=9Kh@%70(OD`*+-p~<$NpOrrB+{F18j7tnc_d+Yl~_XJ2RTcVtA`;qf|*AlqMS zjs0(d2wI+c!m3 zLAP7!us}}npH!$hFA9y_M{ZQC_^;s>xi=4tA?1Luv&4A$q;t^Z5dSgzD3uqnhat?pg8cni%Qm7JU)Dv}!V~nhYsQ+dOSK zk96$mg>ZMc#NyNFWNBOi9txsj_12{ut5uUxT9|(7iT7)bu{lyJ>wCc}a)!%jf$IQX zr^($sPxOsr#YOpS+}7PN1b;cCagD8(*joM1e`mnTOS7S=Cp#=ZZOFP<-x;P>O(x{#5Wh|nwQ4dUjNGS`qmWHilU2@M zZBFB_acjv1oQ-PLWVqL8L1WD~DeL}Rt0n_#FQf1~;@iT?f;BOu{VW~|t*T{WuztP@ zG&eBas>y_PJTy*?lRY>CH)KVm*hC-k87s)*Gx@&6USp3I<=yBq4IYWrwcV)A!@1V> z9#Y+sdp)h146$mrAr&Gj+p5XLkb=E_CVYh4V4e)A=g!m@Zq;NgNrQ6_e&w32oV|Z9 z-#t2)JkJFW?H5u4{#G#$pTo;#|CsRdZf6#sPr^iXhWwL-zms0v6_(83j46(YEoaPdr z!R=mP&lWs^EL2eQ1B#pBw(3it+ipr#`5xc@N3v13gpKs>#Ik=03xlOYa11zGrH~*<5i5-Y2pc zSQn+~28$0Y-GE#pLT&Bwv>3zl>4F#vYt{iF6p;I^w%8ay?n8^7g}6LC$rWO>Rg;M& z8KjuXEWOLjMajl+K&k~p8aftV$FEm2&0=0|XD)q_&G>sHtgg}gVo-(2oA2cm!8Ct0t#&(1r>ILj zl+T8K!WjCHw{62yvwiiv^y+d5JzH=D)`^U=gYxMyt8FPn`-50VG-by|w?;Ju*yS)C5)n|Pb z39A?ERi*VB%AlsXD6U3^W}~-S8_4#E;iI=(jiZ9NKO1*TSQsiF(%y&c19!Lfuv~d9 z905Od+^O@hM%d73p*8`otUT~mYa@nJ{MoWfTdfV&WZP}9-_0zNt<<4q_BU zv|1aj)&{8`n(#1dZCD((+x#FlH{|VY$xF!Jxl0I4k5LTY-CGwebg?{W|g6kd8`@ab7Jrb;~8=C%Y%XJV; z_4^M(OSrk&;>IQECiO}sq9Ybdu&aPJWIG;yrYYbT|b+@fx zze;ZmL5?0O@9Nz15Vr1e&r#Qun+h!~mfuX6VsOZ^46#NypdSaZQkgXOnu)Ozc{AUt zYi_nzSk>$mr5tHdp?(+QrzTb$Q?14MDW2`85IpE75TRL9 zjpsi$UvG{io_n;pYRW~@dWdg+%htkwKa`Y;_>PX8~IlL|5)%3%`$G6*^1QR1_$hYPQSC^W-@D>IzguXx8AHRjwkHn zHs#OdA~wG7HiThop0Olae=HAv(H}xn*m1xeIP>J{eAo2~dB|R;VSKs#9Cto)+WIYy z`aIB5F|EX(Eh+Y$!0u%i>0CPu-9E~kw^STPoD;Q$T(%El(bH%swPIjP`28OOH@y|< z-y))j0xqYcev`aM(Asbjg*duo$WI@-1wf35LL_zv4JN?W3vu&)vFqtEYU zJ%pI+U-uY`H*c-x=9k)6vHwG;v9M`bep+Bj)W&N#K3mjzdnErf2W<|U z?RwN_4tAUg+Z2{Vu@Jt=)`l&AWy{DFA||jB;33Rox!n+GhIa(-KN?vA@wgUf&i5tM z*jm=s_X5ewxg)w?4&-TJn*577rwryUCKFO!*_*tBz>0tpFZFUSmN>b&q`Wy+&ZRt} z`8`7tXnpyAW3$dc3uE&8G>gmS$>(rJ%7DIZH3|t0zXesh{N|y|b-oo>7#suHkGV$S zjV>C8T-0OHyU_2jcW{>Zx%@q>g{NCR%0Y1+_ibI|XrE?;z*S4bml z>*!Ti1&xg;UC#R87sh#vjn2n3WyFHv2B01{eptFciBh~sF?5S+keA=R9g?6bX z@_P%m(aH&cie{OYqG(7`YyAsN?-=)GkecbGcz8ST@5q59Hn#2R05FF z*j|>+7-G5%vX*oxdo$&#e#nfmIuZYae91}A+)U@RM>!Yr{B^O-VVxu2Z-@rx@o8g8 zJ2Wb3k*y`cngoyaLXMsYNwz6{)$6Z*j>+q`>Aq!5PbDk2G->||rtrMQXSo@sxEQS@v{Q`6&dhh;2%3 z^_-uhW*uit{yyJkTNl?X$>(R!Y%j~+`9(aChmx=KtmDiwh=%s#GSZ7&6-tiW{8EaV zOMcxK$>pC;7~;aYtd%Cerko%mXR!r+#QEQcAivaa!%lG7DPOLcE%xW3eW&hb)ayn1 ze=!gBOAXGMZJDOz(-xLF2b>!t1ap#veX&g?OYQzg$!OU3cc-}_jXBRDS;IN9ZJLUZ zK$h?RG*wLBIgF?DG+k3+&m7+L{%viXP;qD8kmpECqMmM-wN!E9MQ2%Zf>i0q{gq;E zDU0<(z7a9U7lgQ#%kAvbwNX`i2IO+?mFU-|dF?qZ24Zu58HOzy?_b4~I7pvl%*`|9 z_4y?9w<0C6ld`{Ph>fSU@lun}yUktE;?<6wj;@11vp|Ww+q%?U*+q_M~=MaVaaBmjR z@rFVmJrmjNXhJ7#o0Lu?q`H?!JKB#1j^m6@%}mxg~im#!CMzrcgd%yT4OL%<5$1YXAFB z@YJ!hv$Sl#em;)dN%!w|?8hawNS?Cz6z)rL_r$2*w*0E{H`C0wHrUI!Wdc=RN-T6j zg)QWAoWJp1)MrkvvmdspoL}xvDBKIVG$!xJ4N+y*MhTQYY-_xqs`H#W&EB%&QDwMr z^i%e%%EPkfW4`Q%%{0yAVsWv4$jve3(CNEy)KhV+c^`J!hZ~LTrz4mcOUc7KIoTYuh+BS3@^*K$kBt^9mr|cDp5n2<9lo^JLq1}W63Q1-lm=) z?dQ2Axj%$r$(rd~gMUcF{d1otFGz_opXTPdjK1ScV%arBcZAmXiiPgfwGZu~F-etd zm(KsbD?Xi*r%hrI=0j~trHHQ{LN>%}EVE|eyDdw|b6FS}Vt$TWk@`Y(wJKI{G>AO4uRqooguIXR;5q&mCoJ)d`% zwy8q7l$&3wtENW~oUIMKyp?~hssg@4bO$+OWzo&)($czhqF&ylG1YdX!DZiQkfUhj zR8U}C*?#}`jm9(ZIblxSiXD8^R5*>!bk$RQ%^60X9CSRv^SJ%;jo7)YOtKQ1D5EY* zx|F>MD1|pg^}}n5j}1!8QJ<5;xj8SP9pV!~vHw3ruhaFy$5$iE+E*EZGdYzlXd&KR z>Xc2mc0?m*Rpq%kCw52hAfTTEcapg#Bt0a&YEudSX4ALDru1IVcP+H zTv!iHXnz}AO)KdY{?-O!q&Fme#vl;Z01JW9sHIE?MDGk!w~x-2WT42tdc zKvHhn#`h?fK{TezvbWsM$?#s$6w90x8SgISW|;Ex^u2J-Bh+I<*3@)MJcRMkcriWH z+2UMd{T^Tr8=RA0b?h*+p28cX>v6HHC4Fc&{4&F*u3q-CFeC>w{f8Ywx6Af+^Owzs ze$V^6$S$jwXJv1M(tH`ui!>Z+s@(jR)R)#BIzy7PX-(=(xTvGP--wM+tFlee&exS14W7+42Mf5`Ahhw}K z%WmZ@Zl8HQ=V*m|z!tO=!>!T9RW22S4C#8v4vP74 z=~LSODcOM!#WK0w-5XWH4V+Do2NXx!TM{c&sePic>xf!{b{35=Lwz3IQf9}Bd_OYkr0p%pm{wXUq&N%FFxZrzBiss zJ(YPq?qnBsTgzsm(D+&A6jz5S>q8}$)knWady<`_9q;13yvK6a6>e~{ViUHfyxTZ? zJM=Y%?AhtNQxCgaE}jcs#zYd+nBU*L{z~wp(*4~9%&_V))t*YmgEev1@d)Owc0|qs z;T+joxc@fkwPZGakE9s4dQ`c>trobq_l-$Me3z|FSNxUjPWpW?I&z%id+im|oX-A5 zpoqJp=b>?Tf0_0<4bzbGb-r=G%*xZiv?yE*$&yw3)K>PqFOSOJNcp)RKJ_x)lFGSx z&F3q*k&ioV{;S+F9eNAPYX(0re`qvH7q)e|em8~wVHeLU=hSv+v3%l1+q|UUbD(Bs ziHG{RPG63>{Uv8ZaeizsD{nxd<7g}=88}xPrt{0JKFN`r<9yQVd*SDMqII3taDO>Z zWLO7Jw`!-!uGGr$B~%W6#4+0xp7q+kWIW_E|Iyhvs4MY6;`WH_3`3gpWQ+NxyZBbm zCA0E-4XxXypa84+wOHb~2>~!boN^2lYUI`5&c^|bKb_T5_g4dt{=w$U%~zYFQ7cOE zdiw%aTd^+u-q(p+MqSsb&7HNj?tPwETh&{2R_4%I=kb@ubbOf*OB!mdwL=l!8g?Yc zNS2fjk2~+r^*4?4#>5Qz?T);KIL(*rmuw{v=+ky(@5WqE_Eso;%HDB)y~Aujoa=qk zoNbkxS!#`p-N(ZW5buXqTAKH@zI{AUA!JZ#H2SG<>J#}_`n9^as zdS64DbA7b_dq;&11JGPKIiWiGdH2?t_!ibq2A`{!V0~57`k)TmI^L1Ocii0?*2L3& zN`Ge!k$LzqZddHBP`9JgA-W*M*w)3#ZixJGdwa*{;{ygddP>C{hpi1?{>pYIdTVDo-g?n zTPuEb;~XxCawuG;;SiY!kj1dE`Z<@gDqF)&Fa}v0Vi4H$V>5G5v;~pFhynSM6gPc> zQs?1v)vbU8d!9xm%W*pG0GtE!Bdc}0GO4Wdp`iOO>x4p!cq2>3kEFDSHzh70lT!C> zu?^hrz+p%`AGgR^dOnk^^WpHrdB5xA@i6LEultsbS06&m|3xPua8~%)=1_i*{HkVbDsA0Zl#@9}MXzH6#S zbHDSd{qeS&Z+pgDrwwF54BsI}JD2xsK?&b+jA@X89Si+2e=d8xm%BC#@mL}{sGoOP z84dlhseDXoTD;r#%u8qpF)%B(QU9(+hf3c~V{c`V-mnb5u&Jo^49CE@uP&C3d26xMLqSocSwTNEu{ZfWsjr zkl}opzFbX)Y)CQj3R24Bn&Y+%Qhdvb06LnDXD|)p%lLD!iIJBxZWnVNe|-#&VOzU- z-x6+p(9M5I4EwtuI~i|@eYsFSuFb|ggeW9$UKq1iH--xDimZfOpHu$gBnzZ2D(qn2 z&1u2Ql)>{ww|$uza#ZaeCVQGLt8vPf@WvgI3hy<_YtX|_BQ>(S7XdQcMQh=SVD-2i z1`xpa<07}jMrx;A9$tY-2dQaz9P$sicD7{{2}n6g+n20@#>AMo$pzA6SX-8^1XWu1 zwF*?|R#K$;~L0 z6hnMwV=eX*9fg7VaZZj3o1H?#S<((==WJe&1J$bJ>wiBkaB-{!q|h>~Cj+-X$g&6JF*D+{e_I= zZp`D&K}%4Fg_(35n_E=s##Y5JZrw@!u@)zMZ+BMUeV1qQMsR?;6@MFLfqYyRF>bJg z%}>Z}H@G<;kju#gN-MVYGJw)|0Z&V}3%OJt0Eju*D>758h({Q^?-0zWO-s9e|UqzO|j)sJr_iV|HixIYR zkF6VFu5%_!?nEeD9t(tAyqCNCLz;$k!q!<8(AWMcI-H$8lfG-|0#Ot{yU&NPYF5s* zw)ZeLwjT&1m+#LDoeIc57v7mi*80#v&Kt+sZvJsG&LO}EHXbPEz3?Y2HDo`Q?D>)g z*;=y8hta)@akiaL&2gr|Xu15D^lrR*?o2iG;oNlz3lyjDUx`~}H{ z9V(pRngc4Z+5EAP#puRz8D|`8t+axy9a#U!8V=9<+8)(BFw-%?QOy0>$~k-7>V8Y` zgScpo7w7ZxzT`FZiHmI^Hvw65&HEu9{MM4Me@|eycK^r5_Iw_Y4UskbKo&%r7Zlr~ z7G4j*4;spJhbD!``}0hibSTdqc09*ht2TAn0`-HnRJDT^2*A`KbtbR_7oc}_7T`PuTE?i@50%XBV*+D`66#||ef8+V#Z;~e8~TeGf@BbPhPf#{E4oMiKj`s^&w2KFSt zr#!`z!#?`)DR(FQ_Hd*1B-8oX@f6MW)$@)+yYkb|Rm`7Xqxm{HsQDHz{!XE|oCHR^ z6z56x9<_P&5%H$?i|s!KnNk}eZBre8opvI_r=k+9gKkqC^v``fCle=mOr+E|m4_K^ zEwKmJcM=1<2R)XDn$iyS9^NaxQyuZ*M_tA&WIFP>N~=!cO~_~1lw)eKAkPo$tD7q; zwvK(TW_^U`spt_8UZ|E*`)OE9A*TS?w(f-6$qp^D!>}uPcJ4=E zm&l#UxiUb5s2=6sb29wLf)ls9ZU^z9*l3y~3ro#?Dy4bsLdJbS4kAAQnQzV)ER%+I zU(BC#bx9yGae92p{CqEU{LHYEAJ=awofV+sa_gRTd7H?>cc*1=#}};RvU$b*STXmp zebqxLo{fHH2I1Jb_&2Bl?zL2)GjZ^?ahGyGW&3#PqVgf_eMq*r^XJ2IW$Gz6-)#X$hk1Ke?$0u?V0F^-ZsUIZ#lAZP?>FsRJ>CvzgX=|GclYt`r&w5iZ!l;5KORM}nhq5iVGU z@o6!9#_cK7&x_sf^yNFTE^iyNod)4Ck@Hj&l7noG$X};<=T7I>a1cB!N6{rP!j1B( z5Bk?Q-j;CoKyEJcdGJ4Ul=r5i7({W0rP%Z++ykLg+(rN!O?@Bm7<}S-4LjdmVt{LG zp+QaNIW;7UQ;tYw zMs77@WR=b~oL#N#LA(SJin0^6?skaxa>n54xg08quPnEBck^QI?w(AXF~r@xI7t9W zsZb)N#BB(@_*Z`3-wF_eLoNaGW)E!zY29$}^5X-=2Ul~*G{rSw|I*!UbhuQ7I}4C2 z_)fH<l1kh!J7g~48V;EYaPse!ob`KuacchXI9b!C<-QLn)=aYDpUZSsS0&D-HL}i2=L6^l7C(`wbhv3W-p*TQnNt<~zX@ cG$r$W{83Y?^!M4I3I1FD`*-*66Zz}^2QE0^_5c6? From 988cd457b7547536cdb23b1f28a5ee32a277c8a4 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 11 May 2021 13:48:43 -0700 Subject: [PATCH 043/110] Reworking webhook_events.md. --- docs/webhook_events.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docs/webhook_events.md b/docs/webhook_events.md index c7b1a2450b..dec9a506cc 100644 --- a/docs/webhook_events.md +++ b/docs/webhook_events.md @@ -244,7 +244,6 @@ Each event will be submitted via HTTP POST to the user provided URL. "items": { "$ref": "#/definitions/TaskDebugFlag" }, - "title": "Debug", "type": "array" }, "job_id": { @@ -1708,7 +1707,6 @@ Each event will be submitted via HTTP POST to the user provided URL. "items": { "$ref": "#/definitions/TaskDebugFlag" }, - "title": "Debug", "type": "array" }, "job_id": { @@ -2315,7 +2313,6 @@ Each event will be submitted via HTTP POST to the user provided URL. "items": { "$ref": "#/definitions/TaskDebugFlag" }, - "title": "Debug", "type": "array" }, "job_id": { @@ -2809,7 +2806,6 @@ Each event will be submitted via HTTP POST to the user provided URL. "items": { "$ref": "#/definitions/TaskDebugFlag" }, - "title": "Debug", "type": "array" }, "job_id": { @@ -3248,7 +3244,6 @@ Each event will be submitted via HTTP POST to the user provided URL. "items": { "$ref": "#/definitions/TaskDebugFlag" }, - "title": "Debug", "type": "array" }, "job_id": { @@ -3661,7 +3656,6 @@ Each event will be submitted via HTTP POST to the user provided URL. "items": { "$ref": "#/definitions/TaskDebugFlag" }, - "title": "Debug", "type": "array" }, "job_id": { @@ -4101,7 +4095,6 @@ Each event will be submitted via HTTP POST to the user provided URL. "items": { "$ref": "#/definitions/TaskDebugFlag" }, - "title": "Debug", "type": "array" }, "job_id": { @@ -5444,7 +5437,6 @@ Each event will be submitted via HTTP POST to the user provided URL. "items": { "$ref": "#/definitions/TaskDebugFlag" }, - "title": "Debug", "type": "array" }, "job_id": { From 7ac914b5a7b3d6fe8e4b278a247525b9ab4ef994 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 11 May 2021 14:00:12 -0700 Subject: [PATCH 044/110] Updating generate_docs to output webhook_events.md file with ascii/LF settings. --- src/pytypes/extra/generate-docs.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/pytypes/extra/generate-docs.py b/src/pytypes/extra/generate-docs.py index 5906837936..72ec266e60 100755 --- a/src/pytypes/extra/generate-docs.py +++ b/src/pytypes/extra/generate-docs.py @@ -61,16 +61,23 @@ EMPTY_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" ZERO_SHA256 = "0" * len(EMPTY_SHA256) +output = open('../../../docs/webhook_events.md', 'w', newline='\n', encoding='ascii') def layer(depth: int, title: str, content: Optional[str] = None) -> None: print(f"{'#' * depth} {title}\n") + output.write(f"{'#' * depth} {title}\n") + output.write("\n") if content is not None: print(f"{content}\n") + output.write(f"{content}\n") + output.write("\n") def typed(depth: int, title: str, content: str, data_type: str) -> None: print(f"{'#' * depth} {title}\n\n```{data_type}\n{content}\n```\n") + output.write(f"{'#' * depth} {title}\n\n```{data_type}\n{content}\n```\n") + output.write("\n") def main() -> None: @@ -279,8 +286,11 @@ def main() -> None: for name in sorted(event_map.keys()): print(f"* [{name}](#{name})") + output.write(f"* [{name}](#{name})") + output.write("\n") print() + output.write("\n") for name in sorted(event_map.keys()): example = event_map[name] @@ -294,6 +304,7 @@ def main() -> None: typed(4, "Schema", example.schema_json(indent=4, sort_keys=True), "json") typed(2, "Full Event Schema", message.schema_json(indent=4, sort_keys=True), "json") + output.close() if __name__ == "__main__": From 1b2f4f8e56995e49211b6dc0b27633247deb2ada Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 11 May 2021 14:05:43 -0700 Subject: [PATCH 045/110] isort and black for generate-docs. --- src/pytypes/extra/generate-docs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pytypes/extra/generate-docs.py b/src/pytypes/extra/generate-docs.py index 72ec266e60..6d364f2c47 100755 --- a/src/pytypes/extra/generate-docs.py +++ b/src/pytypes/extra/generate-docs.py @@ -61,7 +61,7 @@ EMPTY_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" ZERO_SHA256 = "0" * len(EMPTY_SHA256) -output = open('../../../docs/webhook_events.md', 'w', newline='\n', encoding='ascii') +output = open("../../../docs/webhook_events.md", "w", newline="\n", encoding="ascii") def layer(depth: int, title: str, content: Optional[str] = None) -> None: From d2c368273031310db3838c9d832a0ae5ff69f4ed Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 11 May 2021 15:08:35 -0700 Subject: [PATCH 046/110] Allowing path to be command line argument. --- src/pytypes/extra/generate-docs.py | 87 +++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 18 deletions(-) diff --git a/src/pytypes/extra/generate-docs.py b/src/pytypes/extra/generate-docs.py index 6d364f2c47..2b8b3e8ad2 100755 --- a/src/pytypes/extra/generate-docs.py +++ b/src/pytypes/extra/generate-docs.py @@ -3,6 +3,10 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. +from getopt import GetoptError, getopt +from io import TextIOWrapper +from os import path +from sys import argv, exit from typing import List, Optional from uuid import UUID @@ -61,26 +65,54 @@ EMPTY_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" ZERO_SHA256 = "0" * len(EMPTY_SHA256) -output = open("../../../docs/webhook_events.md", "w", newline="\n", encoding="ascii") -def layer(depth: int, title: str, content: Optional[str] = None) -> None: +def layer( + depth: int, title: str, outputfile: TextIOWrapper, content: Optional[str] = None +) -> None: print(f"{'#' * depth} {title}\n") - output.write(f"{'#' * depth} {title}\n") - output.write("\n") + outputfile.write(f"{'#' * depth} {title}\n") + outputfile.write("\n") if content is not None: print(f"{content}\n") - output.write(f"{content}\n") - output.write("\n") + outputfile.write(f"{content}\n") + outputfile.write("\n") -def typed(depth: int, title: str, content: str, data_type: str) -> None: +def typed( + depth: int, title: str, content: str, data_type: str, outputfile: TextIOWrapper +) -> None: print(f"{'#' * depth} {title}\n\n```{data_type}\n{content}\n```\n") - output.write(f"{'#' * depth} {title}\n\n```{data_type}\n{content}\n```\n") - output.write("\n") + outputfile.write(f"{'#' * depth} {title}\n\n```{data_type}\n{content}\n```\n") + outputfile.write("\n") def main() -> None: + + outputfilename = "webhook_events.md" + outputfilepath = "./" + try: + opts, args = getopt(argv[1:], "hi:o:", ["ifile=", "ofile="]) + except GetoptError: + print("Incorrect command line arguments: generate-docs.py -o ") + exit(2) + for opt, arg in opts: + if opt == "-h": + print("Proper command line arguments: generate-docs.py -o ") + exit() + elif opt in ("-o", "--ofile"): + outputfilepath = arg + + outputfiledir = outputfilepath + outputfilename + append_write = "x" + if path.exists(outputfiledir): + append_write = "a" + else: + append_write = "w" + + print("Output file is ", outputfiledir) + outputfile = open(outputfiledir, append_write, newline="\n", encoding="ascii") + task_config = TaskConfig( job_id=UUID(int=0), task=TaskDetails( @@ -268,43 +300,62 @@ def main() -> None: layer( 1, "Webhook Events", + outputfile, "This document describes the basic webhook event subscriptions " "available in OneFuzz", ) layer( 2, "Payload", + outputfile, "Each event will be submitted via HTTP POST to the user provided URL.", ) typed( - 3, "Example", message.json(indent=4, exclude_none=True, sort_keys=True), "json" + 3, + "Example", + message.json(indent=4, exclude_none=True, sort_keys=True), + "json", + outputfile, ) - layer(2, "Event Types (EventType)") + layer(2, "Event Types (EventType)", outputfile) event_map = {get_event_type(x).name: x for x in examples} for name in sorted(event_map.keys()): print(f"* [{name}](#{name})") - output.write(f"* [{name}](#{name})") - output.write("\n") + outputfile.write(f"* [{name}](#{name})") + outputfile.write("\n") print() - output.write("\n") + outputfile.write("\n") for name in sorted(event_map.keys()): example = event_map[name] - layer(3, name) + layer(3, name, outputfile) typed( 4, "Example", example.json(indent=4, exclude_none=True, sort_keys=True), "json", + outputfile, + ) + typed( + 4, + "Schema", + example.schema_json(indent=4, sort_keys=True), + "json", + outputfile, ) - typed(4, "Schema", example.schema_json(indent=4, sort_keys=True), "json") - typed(2, "Full Event Schema", message.schema_json(indent=4, sort_keys=True), "json") - output.close() + typed( + 2, + "Full Event Schema", + message.schema_json(indent=4, sort_keys=True), + "json", + outputfile, + ) + outputfile.close() if __name__ == "__main__": From 33f625a7f4741408c5f6649cceb26c832684ac3c Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 11 May 2021 15:26:28 -0700 Subject: [PATCH 047/110] Fixing file type. --- src/pytypes/extra/generate-docs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pytypes/extra/generate-docs.py b/src/pytypes/extra/generate-docs.py index 2b8b3e8ad2..da8aeaa333 100755 --- a/src/pytypes/extra/generate-docs.py +++ b/src/pytypes/extra/generate-docs.py @@ -4,7 +4,7 @@ # Licensed under the MIT License. from getopt import GetoptError, getopt -from io import TextIOWrapper +from io import FileIO from os import path from sys import argv, exit from typing import List, Optional @@ -68,7 +68,7 @@ def layer( - depth: int, title: str, outputfile: TextIOWrapper, content: Optional[str] = None + depth: int, title: str, outputfile: FileIO, content: Optional[str] = None ) -> None: print(f"{'#' * depth} {title}\n") outputfile.write(f"{'#' * depth} {title}\n") @@ -80,7 +80,7 @@ def layer( def typed( - depth: int, title: str, content: str, data_type: str, outputfile: TextIOWrapper + depth: int, title: str, content: str, data_type: str, outputfile: FileIO ) -> None: print(f"{'#' * depth} {title}\n\n```{data_type}\n{content}\n```\n") outputfile.write(f"{'#' * depth} {title}\n\n```{data_type}\n{content}\n```\n") From 81a311c492324df6b71e6708fe9a6dea846cfb47 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 11 May 2021 15:43:36 -0700 Subject: [PATCH 048/110] Static variable for file directory. --- src/pytypes/extra/generate-docs.py | 51 ++++++++++++++---------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/src/pytypes/extra/generate-docs.py b/src/pytypes/extra/generate-docs.py index da8aeaa333..f9e316bc9d 100755 --- a/src/pytypes/extra/generate-docs.py +++ b/src/pytypes/extra/generate-docs.py @@ -65,26 +65,27 @@ EMPTY_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" ZERO_SHA256 = "0" * len(EMPTY_SHA256) - +OUTPUT_FILE_DIR = "./webhook_events.md" +OUTPUT_FILE = open(OUTPUT_FILE_DIR, "w", newline="\n", encoding="ascii") def layer( - depth: int, title: str, outputfile: FileIO, content: Optional[str] = None + depth: int, title: str, content: Optional[str] = None ) -> None: print(f"{'#' * depth} {title}\n") - outputfile.write(f"{'#' * depth} {title}\n") - outputfile.write("\n") + OUTPUT_FILE.write(f"{'#' * depth} {title}\n") + OUTPUT_FILE.write("\n") if content is not None: print(f"{content}\n") - outputfile.write(f"{content}\n") - outputfile.write("\n") + OUTPUT_FILE.write(f"{content}\n") + OUTPUT_FILE.write("\n") def typed( - depth: int, title: str, content: str, data_type: str, outputfile: FileIO + depth: int, title: str, content: str, data_type: str ) -> None: print(f"{'#' * depth} {title}\n\n```{data_type}\n{content}\n```\n") - outputfile.write(f"{'#' * depth} {title}\n\n```{data_type}\n{content}\n```\n") - outputfile.write("\n") + OUTPUT_FILE.write(f"{'#' * depth} {title}\n\n```{data_type}\n{content}\n```\n") + OUTPUT_FILE.write("\n") def main() -> None: @@ -103,15 +104,15 @@ def main() -> None: elif opt in ("-o", "--ofile"): outputfilepath = arg - outputfiledir = outputfilepath + outputfilename + OUTPUT_FILE_DIR = outputfilepath + outputfilename append_write = "x" - if path.exists(outputfiledir): + if path.exists(OUTPUT_FILE_DIR): append_write = "a" else: append_write = "w" - print("Output file is ", outputfiledir) - outputfile = open(outputfiledir, append_write, newline="\n", encoding="ascii") + print("Output file is ", OUTPUT_FILE_DIR) + OUTPUT_FILE = open(OUTPUT_FILE_DIR, append_write, newline="\n", encoding="ascii") task_config = TaskConfig( job_id=UUID(int=0), @@ -300,14 +301,12 @@ def main() -> None: layer( 1, "Webhook Events", - outputfile, "This document describes the basic webhook event subscriptions " "available in OneFuzz", ) layer( 2, "Payload", - outputfile, "Each event will be submitted via HTTP POST to the user provided URL.", ) @@ -316,46 +315,42 @@ def main() -> None: "Example", message.json(indent=4, exclude_none=True, sort_keys=True), "json", - outputfile, ) - layer(2, "Event Types (EventType)", outputfile) + layer(2, "Event Types (EventType)") event_map = {get_event_type(x).name: x for x in examples} for name in sorted(event_map.keys()): print(f"* [{name}](#{name})") - outputfile.write(f"* [{name}](#{name})") - outputfile.write("\n") + OUTPUT_FILE.write(f"* [{name}](#{name})") + OUTPUT_FILE.write("\n") print() - outputfile.write("\n") + OUTPUT_FILE.write("\n") for name in sorted(event_map.keys()): example = event_map[name] - layer(3, name, outputfile) + layer(3, name) typed( 4, "Example", example.json(indent=4, exclude_none=True, sort_keys=True), - "json", - outputfile, + "json" ) typed( 4, "Schema", example.schema_json(indent=4, sort_keys=True), - "json", - outputfile, + "json" ) typed( 2, "Full Event Schema", message.schema_json(indent=4, sort_keys=True), - "json", - outputfile, + "json" ) - outputfile.close() + OUTPUT_FILE.close() if __name__ == "__main__": From 4d68273c21dbb0f710927c66a678d1ba9b517375 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 11 May 2021 15:51:02 -0700 Subject: [PATCH 049/110] Isort and black for generate_docs --- src/pytypes/extra/generate-docs.py | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/src/pytypes/extra/generate-docs.py b/src/pytypes/extra/generate-docs.py index f9e316bc9d..ec73b63dc5 100755 --- a/src/pytypes/extra/generate-docs.py +++ b/src/pytypes/extra/generate-docs.py @@ -68,9 +68,8 @@ OUTPUT_FILE_DIR = "./webhook_events.md" OUTPUT_FILE = open(OUTPUT_FILE_DIR, "w", newline="\n", encoding="ascii") -def layer( - depth: int, title: str, content: Optional[str] = None -) -> None: + +def layer(depth: int, title: str, content: Optional[str] = None) -> None: print(f"{'#' * depth} {title}\n") OUTPUT_FILE.write(f"{'#' * depth} {title}\n") OUTPUT_FILE.write("\n") @@ -80,9 +79,7 @@ def layer( OUTPUT_FILE.write("\n") -def typed( - depth: int, title: str, content: str, data_type: str -) -> None: +def typed(depth: int, title: str, content: str, data_type: str) -> None: print(f"{'#' * depth} {title}\n\n```{data_type}\n{content}\n```\n") OUTPUT_FILE.write(f"{'#' * depth} {title}\n\n```{data_type}\n{content}\n```\n") OUTPUT_FILE.write("\n") @@ -335,21 +332,11 @@ def main() -> None: 4, "Example", example.json(indent=4, exclude_none=True, sort_keys=True), - "json" - ) - typed( - 4, - "Schema", - example.schema_json(indent=4, sort_keys=True), - "json" + "json", ) + typed(4, "Schema", example.schema_json(indent=4, sort_keys=True), "json") - typed( - 2, - "Full Event Schema", - message.schema_json(indent=4, sort_keys=True), - "json" - ) + typed(2, "Full Event Schema", message.schema_json(indent=4, sort_keys=True), "json") OUTPUT_FILE.close() From 89cf455956693a68ace2717ad5d71f121eb8da9c Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 11 May 2021 15:59:28 -0700 Subject: [PATCH 050/110] Removing IO statements. --- src/pytypes/extra/generate-docs.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pytypes/extra/generate-docs.py b/src/pytypes/extra/generate-docs.py index ec73b63dc5..475436f5eb 100755 --- a/src/pytypes/extra/generate-docs.py +++ b/src/pytypes/extra/generate-docs.py @@ -4,7 +4,6 @@ # Licensed under the MIT License. from getopt import GetoptError, getopt -from io import FileIO from os import path from sys import argv, exit from typing import List, Optional From 6b898d494286034d6905e24489d1290004aa4811 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 11 May 2021 16:11:24 -0700 Subject: [PATCH 051/110] Replacing Webhook events --- docs/webhook_events.md | 5861 ---------------------------------------- 1 file changed, 5861 deletions(-) diff --git a/docs/webhook_events.md b/docs/webhook_events.md index dec9a506cc..cb6fae74b7 100644 --- a/docs/webhook_events.md +++ b/docs/webhook_events.md @@ -1,28 +1,3 @@ -# Webhook Events - -This document describes the basic webhook event subscriptions available in OneFuzz - -## Payload - -Each event will be submitted via HTTP POST to the user provided URL. - -### Example - -```json -{ - "event": { - "ping_id": "00000000-0000-0000-0000-000000000000" - }, - "event_id": "00000000-0000-0000-0000-000000000000", - "event_type": "ping", - "instance_id": "00000000-0000-0000-0000-000000000000", - "instance_name": "example", - "webhook_id": "00000000-0000-0000-0000-000000000000" -} -``` - -## Event Types (EventType) - * [crash_reported](#crash_reported) * [file_added](#file_added) * [job_created](#job_created) @@ -47,5839 +22,3 @@ Each event will be submitted via HTTP POST to the user provided URL. * [task_state_updated](#task_state_updated) * [task_stopped](#task_stopped) -### crash_reported - -#### Example - -```json -{ - "container": "container-name", - "filename": "example.json", - "report": { - "asan_log": "example asan log", - "call_stack": [ - "#0 line", - "#1 line", - "#2 line" - ], - "call_stack_sha256": "0000000000000000000000000000000000000000000000000000000000000000", - "crash_site": "example crash site", - "crash_type": "example crash report type", - "executable": "fuzz.exe", - "input_blob": { - "account": "contoso-storage-account", - "container": "crashes", - "name": "input.txt" - }, - "input_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - "job_id": "00000000-0000-0000-0000-000000000000", - "scariness_description": "example-scariness", - "scariness_score": 10, - "task_id": "00000000-0000-0000-0000-000000000000" - } -} -``` - -#### Schema - -```json -{ - "definitions": { - "BlobRef": { - "properties": { - "account": { - "title": "Account", - "type": "string" - }, - "container": { - "title": "Container", - "type": "string" - }, - "name": { - "title": "Name", - "type": "string" - } - }, - "required": [ - "account", - "container", - "name" - ], - "title": "BlobRef", - "type": "object" - }, - "ContainerType": { - "description": "An enumeration.", - "enum": [ - "analysis", - "coverage", - "crashes", - "inputs", - "no_repro", - "readonly_inputs", - "reports", - "setup", - "tools", - "unique_inputs", - "unique_reports", - "regression_reports" - ], - "title": "ContainerType" - }, - "Report": { - "properties": { - "asan_log": { - "title": "Asan Log", - "type": "string" - }, - "call_stack": { - "items": { - "type": "string" - }, - "title": "Call Stack", - "type": "array" - }, - "call_stack_sha256": { - "title": "Call Stack Sha256", - "type": "string" - }, - "crash_site": { - "title": "Crash Site", - "type": "string" - }, - "crash_type": { - "title": "Crash Type", - "type": "string" - }, - "executable": { - "title": "Executable", - "type": "string" - }, - "input_blob": { - "$ref": "#/definitions/BlobRef" - }, - "input_sha256": { - "title": "Input Sha256", - "type": "string" - }, - "input_url": { - "title": "Input Url", - "type": "string" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "minimized_stack": { - "items": { - "type": "string" - }, - "title": "Minimized Stack", - "type": "array" - }, - "minimized_stack_function_names": { - "items": { - "type": "string" - }, - "title": "Minimized Stack Function Names", - "type": "array" - }, - "minimized_stack_function_names_sha256": { - "title": "Minimized Stack Function Names Sha256", - "type": "string" - }, - "minimized_stack_sha256": { - "title": "Minimized Stack Sha256", - "type": "string" - }, - "scariness_description": { - "title": "Scariness Description", - "type": "string" - }, - "scariness_score": { - "title": "Scariness Score", - "type": "integer" - }, - "task_id": { - "format": "uuid", - "title": "Task Id", - "type": "string" - } - }, - "required": [ - "executable", - "crash_type", - "crash_site", - "call_stack", - "call_stack_sha256", - "input_sha256", - "task_id", - "job_id" - ], - "title": "Report", - "type": "object" - }, - "StatsFormat": { - "description": "An enumeration.", - "enum": [ - "AFL" - ], - "title": "StatsFormat" - }, - "TaskConfig": { - "properties": { - "colocate": { - "title": "Colocate", - "type": "boolean" - }, - "containers": { - "items": { - "$ref": "#/definitions/TaskContainers" - }, - "title": "Containers", - "type": "array" - }, - "debug": { - "items": { - "$ref": "#/definitions/TaskDebugFlag" - }, - "type": "array" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "pool": { - "$ref": "#/definitions/TaskPool" - }, - "prereq_tasks": { - "items": { - "format": "uuid", - "type": "string" - }, - "title": "Prereq Tasks", - "type": "array" - }, - "tags": { - "additionalProperties": { - "type": "string" - }, - "title": "Tags", - "type": "object" - }, - "task": { - "$ref": "#/definitions/TaskDetails" - }, - "vm": { - "$ref": "#/definitions/TaskVm" - } - }, - "required": [ - "job_id", - "task", - "containers", - "tags" - ], - "title": "TaskConfig", - "type": "object" - }, - "TaskContainers": { - "properties": { - "name": { - "title": "Name", - "type": "string" - }, - "type": { - "$ref": "#/definitions/ContainerType" - } - }, - "required": [ - "type", - "name" - ], - "title": "TaskContainers", - "type": "object" - }, - "TaskDebugFlag": { - "description": "An enumeration.", - "enum": [ - "keep_node_on_failure", - "keep_node_on_completion" - ], - "title": "TaskDebugFlag" - }, - "TaskDetails": { - "properties": { - "analyzer_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Analyzer Env", - "type": "object" - }, - "analyzer_exe": { - "title": "Analyzer Exe", - "type": "string" - }, - "analyzer_options": { - "items": { - "type": "string" - }, - "title": "Analyzer Options", - "type": "array" - }, - "check_asan_log": { - "title": "Check Asan Log", - "type": "boolean" - }, - "check_debugger": { - "default": true, - "title": "Check Debugger", - "type": "boolean" - }, - "check_fuzzer_help": { - "title": "Check Fuzzer Help", - "type": "boolean" - }, - "check_retry_count": { - "title": "Check Retry Count", - "type": "integer" - }, - "duration": { - "title": "Duration", - "type": "integer" - }, - "ensemble_sync_delay": { - "title": "Ensemble Sync Delay", - "type": "integer" - }, - "expect_crash_on_failure": { - "title": "Expect Crash On Failure", - "type": "boolean" - }, - "generator_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Generator Env", - "type": "object" - }, - "generator_exe": { - "title": "Generator Exe", - "type": "string" - }, - "generator_options": { - "items": { - "type": "string" - }, - "title": "Generator Options", - "type": "array" - }, - "minimized_stack_depth": { - "title": "Minimized Stack Depth", - "type": "integer" - }, - "preserve_existing_outputs": { - "title": "Preserve Existing Outputs", - "type": "boolean" - }, - "reboot_after_setup": { - "title": "Reboot After Setup", - "type": "boolean" - }, - "rename_output": { - "title": "Rename Output", - "type": "boolean" - }, - "report_list": { - "items": { - "type": "string" - }, - "title": "Report List", - "type": "array" - }, - "stats_file": { - "title": "Stats File", - "type": "string" - }, - "stats_format": { - "$ref": "#/definitions/StatsFormat" - }, - "supervisor_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Supervisor Env", - "type": "object" - }, - "supervisor_exe": { - "title": "Supervisor Exe", - "type": "string" - }, - "supervisor_input_marker": { - "title": "Supervisor Input Marker", - "type": "string" - }, - "supervisor_options": { - "items": { - "type": "string" - }, - "title": "Supervisor Options", - "type": "array" - }, - "target_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Target Env", - "type": "object" - }, - "target_exe": { - "title": "Target Exe", - "type": "string" - }, - "target_options": { - "items": { - "type": "string" - }, - "title": "Target Options", - "type": "array" - }, - "target_options_merge": { - "title": "Target Options Merge", - "type": "boolean" - }, - "target_timeout": { - "title": "Target Timeout", - "type": "integer" - }, - "target_workers": { - "title": "Target Workers", - "type": "integer" - }, - "type": { - "$ref": "#/definitions/TaskType" - }, - "wait_for_files": { - "$ref": "#/definitions/ContainerType" - } - }, - "required": [ - "type", - "duration" - ], - "title": "TaskDetails", - "type": "object" - }, - "TaskPool": { - "properties": { - "count": { - "title": "Count", - "type": "integer" - }, - "pool_name": { - "title": "Pool Name", - "type": "string" - } - }, - "required": [ - "count", - "pool_name" - ], - "title": "TaskPool", - "type": "object" - }, - "TaskType": { - "description": "An enumeration.", - "enum": [ - "libfuzzer_fuzz", - "libfuzzer_coverage", - "libfuzzer_crash_report", - "libfuzzer_merge", - "libfuzzer_regression", - "generic_analysis", - "generic_supervisor", - "generic_merge", - "generic_generator", - "generic_crash_report", - "generic_regression" - ], - "title": "TaskType" - }, - "TaskVm": { - "properties": { - "count": { - "default": 1, - "title": "Count", - "type": "integer" - }, - "image": { - "title": "Image", - "type": "string" - }, - "reboot_after_setup": { - "title": "Reboot After Setup", - "type": "boolean" - }, - "region": { - "title": "Region", - "type": "string" - }, - "sku": { - "title": "Sku", - "type": "string" - }, - "spot_instances": { - "default": false, - "title": "Spot Instances", - "type": "boolean" - } - }, - "required": [ - "region", - "sku", - "image" - ], - "title": "TaskVm", - "type": "object" - } - }, - "properties": { - "container": { - "title": "Container", - "type": "string" - }, - "filename": { - "title": "Filename", - "type": "string" - }, - "report": { - "$ref": "#/definitions/Report" - }, - "task_config": { - "$ref": "#/definitions/TaskConfig" - } - }, - "required": [ - "report", - "container", - "filename" - ], - "title": "EventCrashReported", - "type": "object" -} -``` - -### file_added - -#### Example - -```json -{ - "container": "container-name", - "filename": "example.txt" -} -``` - -#### Schema - -```json -{ - "properties": { - "container": { - "title": "Container", - "type": "string" - }, - "filename": { - "title": "Filename", - "type": "string" - } - }, - "required": [ - "container", - "filename" - ], - "title": "EventFileAdded", - "type": "object" -} -``` - -### job_created - -#### Example - -```json -{ - "config": { - "build": "build 1", - "duration": 24, - "name": "example name", - "project": "example project" - }, - "job_id": "00000000-0000-0000-0000-000000000000" -} -``` - -#### Schema - -```json -{ - "definitions": { - "JobConfig": { - "properties": { - "build": { - "title": "Build", - "type": "string" - }, - "duration": { - "title": "Duration", - "type": "integer" - }, - "name": { - "title": "Name", - "type": "string" - }, - "project": { - "title": "Project", - "type": "string" - } - }, - "required": [ - "project", - "name", - "build", - "duration" - ], - "title": "JobConfig", - "type": "object" - }, - "UserInfo": { - "properties": { - "application_id": { - "format": "uuid", - "title": "Application Id", - "type": "string" - }, - "object_id": { - "format": "uuid", - "title": "Object Id", - "type": "string" - }, - "upn": { - "title": "Upn", - "type": "string" - } - }, - "title": "UserInfo", - "type": "object" - } - }, - "properties": { - "config": { - "$ref": "#/definitions/JobConfig" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "user_info": { - "$ref": "#/definitions/UserInfo" - } - }, - "required": [ - "job_id", - "config" - ], - "title": "EventJobCreated", - "type": "object" -} -``` - -### job_stopped - -#### Example - -```json -{ - "config": { - "build": "build 1", - "duration": 24, - "name": "example name", - "project": "example project" - }, - "job_id": "00000000-0000-0000-0000-000000000000", - "task_info": [ - { - "error": { - "code": 468, - "errors": [ - "example error message" - ] - }, - "task_id": "00000000-0000-0000-0000-000000000000", - "task_type": "libfuzzer_fuzz" - }, - { - "task_id": "00000000-0000-0000-0000-000000000001", - "task_type": "libfuzzer_coverage" - } - ] -} -``` - -#### Schema - -```json -{ - "definitions": { - "Error": { - "properties": { - "code": { - "$ref": "#/definitions/ErrorCode" - }, - "errors": { - "items": { - "type": "string" - }, - "title": "Errors", - "type": "array" - } - }, - "required": [ - "code", - "errors" - ], - "title": "Error", - "type": "object" - }, - "ErrorCode": { - "description": "An enumeration.", - "enum": [ - 450, - 451, - 452, - 453, - 454, - 455, - 456, - 457, - 458, - 459, - 460, - 461, - 462, - 463, - 464, - 465, - 467, - 468, - 469, - 470, - 471, - 472 - ], - "title": "ErrorCode" - }, - "JobConfig": { - "properties": { - "build": { - "title": "Build", - "type": "string" - }, - "duration": { - "title": "Duration", - "type": "integer" - }, - "name": { - "title": "Name", - "type": "string" - }, - "project": { - "title": "Project", - "type": "string" - } - }, - "required": [ - "project", - "name", - "build", - "duration" - ], - "title": "JobConfig", - "type": "object" - }, - "JobTaskStopped": { - "properties": { - "error": { - "$ref": "#/definitions/Error" - }, - "task_id": { - "format": "uuid", - "title": "Task Id", - "type": "string" - }, - "task_type": { - "$ref": "#/definitions/TaskType" - } - }, - "required": [ - "task_id", - "task_type" - ], - "title": "JobTaskStopped", - "type": "object" - }, - "TaskType": { - "description": "An enumeration.", - "enum": [ - "libfuzzer_fuzz", - "libfuzzer_coverage", - "libfuzzer_crash_report", - "libfuzzer_merge", - "libfuzzer_regression", - "generic_analysis", - "generic_supervisor", - "generic_merge", - "generic_generator", - "generic_crash_report", - "generic_regression" - ], - "title": "TaskType" - }, - "UserInfo": { - "properties": { - "application_id": { - "format": "uuid", - "title": "Application Id", - "type": "string" - }, - "object_id": { - "format": "uuid", - "title": "Object Id", - "type": "string" - }, - "upn": { - "title": "Upn", - "type": "string" - } - }, - "title": "UserInfo", - "type": "object" - } - }, - "properties": { - "config": { - "$ref": "#/definitions/JobConfig" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "task_info": { - "items": { - "$ref": "#/definitions/JobTaskStopped" - }, - "title": "Task Info", - "type": "array" - }, - "user_info": { - "$ref": "#/definitions/UserInfo" - } - }, - "required": [ - "job_id", - "config" - ], - "title": "EventJobStopped", - "type": "object" -} -``` - -### node_created - -#### Example - -```json -{ - "machine_id": "00000000-0000-0000-0000-000000000000", - "pool_name": "example" -} -``` - -#### Schema - -```json -{ - "properties": { - "machine_id": { - "format": "uuid", - "title": "Machine Id", - "type": "string" - }, - "pool_name": { - "title": "Pool Name", - "type": "string" - }, - "scaleset_id": { - "format": "uuid", - "title": "Scaleset Id", - "type": "string" - } - }, - "required": [ - "machine_id", - "pool_name" - ], - "title": "EventNodeCreated", - "type": "object" -} -``` - -### node_deleted - -#### Example - -```json -{ - "machine_id": "00000000-0000-0000-0000-000000000000", - "pool_name": "example" -} -``` - -#### Schema - -```json -{ - "properties": { - "machine_id": { - "format": "uuid", - "title": "Machine Id", - "type": "string" - }, - "pool_name": { - "title": "Pool Name", - "type": "string" - }, - "scaleset_id": { - "format": "uuid", - "title": "Scaleset Id", - "type": "string" - } - }, - "required": [ - "machine_id", - "pool_name" - ], - "title": "EventNodeDeleted", - "type": "object" -} -``` - -### node_heartbeat - -#### Example - -```json -{ - "machine_id": "00000000-0000-0000-0000-000000000000", - "pool_name": "example" -} -``` - -#### Schema - -```json -{ - "properties": { - "machine_id": { - "format": "uuid", - "title": "Machine Id", - "type": "string" - }, - "pool_name": { - "title": "Pool Name", - "type": "string" - }, - "scaleset_id": { - "format": "uuid", - "title": "Scaleset Id", - "type": "string" - } - }, - "required": [ - "machine_id", - "pool_name" - ], - "title": "EventNodeHeartbeat", - "type": "object" -} -``` - -### node_state_updated - -#### Example - -```json -{ - "machine_id": "00000000-0000-0000-0000-000000000000", - "pool_name": "example", - "state": "setting_up" -} -``` - -#### Schema - -```json -{ - "definitions": { - "NodeState": { - "description": "An enumeration.", - "enum": [ - "init", - "free", - "setting_up", - "rebooting", - "ready", - "busy", - "done", - "shutdown", - "halt" - ], - "title": "NodeState" - } - }, - "properties": { - "machine_id": { - "format": "uuid", - "title": "Machine Id", - "type": "string" - }, - "pool_name": { - "title": "Pool Name", - "type": "string" - }, - "scaleset_id": { - "format": "uuid", - "title": "Scaleset Id", - "type": "string" - }, - "state": { - "$ref": "#/definitions/NodeState" - } - }, - "required": [ - "machine_id", - "pool_name", - "state" - ], - "title": "EventNodeStateUpdated", - "type": "object" -} -``` - -### ping - -#### Example - -```json -{ - "ping_id": "00000000-0000-0000-0000-000000000000" -} -``` - -#### Schema - -```json -{ - "properties": { - "ping_id": { - "format": "uuid", - "title": "Ping Id", - "type": "string" - } - }, - "required": [ - "ping_id" - ], - "title": "EventPing", - "type": "object" -} -``` - -### pool_created - -#### Example - -```json -{ - "arch": "x86_64", - "managed": true, - "os": "linux", - "pool_name": "example" -} -``` - -#### Schema - -```json -{ - "definitions": { - "Architecture": { - "description": "An enumeration.", - "enum": [ - "x86_64" - ], - "title": "Architecture" - }, - "AutoScaleConfig": { - "properties": { - "ephemeral_os_disks": { - "default": false, - "title": "Ephemeral Os Disks", - "type": "boolean" - }, - "image": { - "title": "Image", - "type": "string" - }, - "max_size": { - "title": "Max Size", - "type": "integer" - }, - "min_size": { - "default": 0, - "title": "Min Size", - "type": "integer" - }, - "region": { - "title": "Region", - "type": "string" - }, - "scaleset_size": { - "title": "Scaleset Size", - "type": "integer" - }, - "spot_instances": { - "default": false, - "title": "Spot Instances", - "type": "boolean" - }, - "vm_sku": { - "title": "Vm Sku", - "type": "string" - } - }, - "required": [ - "image", - "scaleset_size", - "vm_sku" - ], - "title": "AutoScaleConfig", - "type": "object" - }, - "OS": { - "description": "An enumeration.", - "enum": [ - "windows", - "linux" - ], - "title": "OS" - } - }, - "properties": { - "arch": { - "$ref": "#/definitions/Architecture" - }, - "autoscale": { - "$ref": "#/definitions/AutoScaleConfig" - }, - "managed": { - "title": "Managed", - "type": "boolean" - }, - "os": { - "$ref": "#/definitions/OS" - }, - "pool_name": { - "title": "Pool Name", - "type": "string" - } - }, - "required": [ - "pool_name", - "os", - "arch", - "managed" - ], - "title": "EventPoolCreated", - "type": "object" -} -``` - -### pool_deleted - -#### Example - -```json -{ - "pool_name": "example" -} -``` - -#### Schema - -```json -{ - "properties": { - "pool_name": { - "title": "Pool Name", - "type": "string" - } - }, - "required": [ - "pool_name" - ], - "title": "EventPoolDeleted", - "type": "object" -} -``` - -### proxy_created - -#### Example - -```json -{ - "proxy_id": "00000000-0000-0000-0000-000000000000", - "region": "eastus" -} -``` - -#### Schema - -```json -{ - "properties": { - "proxy_id": { - "format": "uuid", - "title": "Proxy Id", - "type": "string" - }, - "region": { - "title": "Region", - "type": "string" - } - }, - "required": [ - "region", - "proxy_id" - ], - "title": "EventProxyCreated", - "type": "object" -} -``` - -### proxy_deleted - -#### Example - -```json -{ - "proxy_id": "00000000-0000-0000-0000-000000000000", - "region": "eastus" -} -``` - -#### Schema - -```json -{ - "properties": { - "proxy_id": { - "format": "uuid", - "title": "Proxy Id", - "type": "string" - }, - "region": { - "title": "Region", - "type": "string" - } - }, - "required": [ - "region", - "proxy_id" - ], - "title": "EventProxyDeleted", - "type": "object" -} -``` - -### proxy_failed - -#### Example - -```json -{ - "error": { - "code": 472, - "errors": [ - "example error message" - ] - }, - "proxy_id": "00000000-0000-0000-0000-000000000000", - "region": "eastus" -} -``` - -#### Schema - -```json -{ - "definitions": { - "Error": { - "properties": { - "code": { - "$ref": "#/definitions/ErrorCode" - }, - "errors": { - "items": { - "type": "string" - }, - "title": "Errors", - "type": "array" - } - }, - "required": [ - "code", - "errors" - ], - "title": "Error", - "type": "object" - }, - "ErrorCode": { - "description": "An enumeration.", - "enum": [ - 450, - 451, - 452, - 453, - 454, - 455, - 456, - 457, - 458, - 459, - 460, - 461, - 462, - 463, - 464, - 465, - 467, - 468, - 469, - 470, - 471, - 472 - ], - "title": "ErrorCode" - } - }, - "properties": { - "error": { - "$ref": "#/definitions/Error" - }, - "proxy_id": { - "format": "uuid", - "title": "Proxy Id", - "type": "string" - }, - "region": { - "title": "Region", - "type": "string" - } - }, - "required": [ - "region", - "proxy_id", - "error" - ], - "title": "EventProxyFailed", - "type": "object" -} -``` - -### regression_reported - -#### Example - -```json -{ - "container": "container-name", - "filename": "example.json", - "regression_report": { - "crash_test_result": { - "crash_report": { - "asan_log": "example asan log", - "call_stack": [ - "#0 line", - "#1 line", - "#2 line" - ], - "call_stack_sha256": "0000000000000000000000000000000000000000000000000000000000000000", - "crash_site": "example crash site", - "crash_type": "example crash report type", - "executable": "fuzz.exe", - "input_blob": { - "account": "contoso-storage-account", - "container": "crashes", - "name": "input.txt" - }, - "input_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - "job_id": "00000000-0000-0000-0000-000000000000", - "scariness_description": "example-scariness", - "scariness_score": 10, - "task_id": "00000000-0000-0000-0000-000000000000" - } - }, - "original_crash_test_result": { - "crash_report": { - "asan_log": "example asan log", - "call_stack": [ - "#0 line", - "#1 line", - "#2 line" - ], - "call_stack_sha256": "0000000000000000000000000000000000000000000000000000000000000000", - "crash_site": "example crash site", - "crash_type": "example crash report type", - "executable": "fuzz.exe", - "input_blob": { - "account": "contoso-storage-account", - "container": "crashes", - "name": "input.txt" - }, - "input_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - "job_id": "00000000-0000-0000-0000-000000000000", - "scariness_description": "example-scariness", - "scariness_score": 10, - "task_id": "00000000-0000-0000-0000-000000000000" - } - } - } -} -``` - -#### Schema - -```json -{ - "definitions": { - "BlobRef": { - "properties": { - "account": { - "title": "Account", - "type": "string" - }, - "container": { - "title": "Container", - "type": "string" - }, - "name": { - "title": "Name", - "type": "string" - } - }, - "required": [ - "account", - "container", - "name" - ], - "title": "BlobRef", - "type": "object" - }, - "ContainerType": { - "description": "An enumeration.", - "enum": [ - "analysis", - "coverage", - "crashes", - "inputs", - "no_repro", - "readonly_inputs", - "reports", - "setup", - "tools", - "unique_inputs", - "unique_reports", - "regression_reports" - ], - "title": "ContainerType" - }, - "CrashTestResult": { - "properties": { - "crash_report": { - "$ref": "#/definitions/Report" - }, - "no_repro": { - "$ref": "#/definitions/NoReproReport" - } - }, - "title": "CrashTestResult", - "type": "object" - }, - "NoReproReport": { - "properties": { - "error": { - "title": "Error", - "type": "string" - }, - "executable": { - "title": "Executable", - "type": "string" - }, - "input_blob": { - "$ref": "#/definitions/BlobRef" - }, - "input_sha256": { - "title": "Input Sha256", - "type": "string" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "task_id": { - "format": "uuid", - "title": "Task Id", - "type": "string" - }, - "tries": { - "title": "Tries", - "type": "integer" - } - }, - "required": [ - "input_sha256", - "executable", - "task_id", - "job_id", - "tries" - ], - "title": "NoReproReport", - "type": "object" - }, - "RegressionReport": { - "properties": { - "crash_test_result": { - "$ref": "#/definitions/CrashTestResult" - }, - "original_crash_test_result": { - "$ref": "#/definitions/CrashTestResult" - } - }, - "required": [ - "crash_test_result" - ], - "title": "RegressionReport", - "type": "object" - }, - "Report": { - "properties": { - "asan_log": { - "title": "Asan Log", - "type": "string" - }, - "call_stack": { - "items": { - "type": "string" - }, - "title": "Call Stack", - "type": "array" - }, - "call_stack_sha256": { - "title": "Call Stack Sha256", - "type": "string" - }, - "crash_site": { - "title": "Crash Site", - "type": "string" - }, - "crash_type": { - "title": "Crash Type", - "type": "string" - }, - "executable": { - "title": "Executable", - "type": "string" - }, - "input_blob": { - "$ref": "#/definitions/BlobRef" - }, - "input_sha256": { - "title": "Input Sha256", - "type": "string" - }, - "input_url": { - "title": "Input Url", - "type": "string" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "minimized_stack": { - "items": { - "type": "string" - }, - "title": "Minimized Stack", - "type": "array" - }, - "minimized_stack_function_names": { - "items": { - "type": "string" - }, - "title": "Minimized Stack Function Names", - "type": "array" - }, - "minimized_stack_function_names_sha256": { - "title": "Minimized Stack Function Names Sha256", - "type": "string" - }, - "minimized_stack_sha256": { - "title": "Minimized Stack Sha256", - "type": "string" - }, - "scariness_description": { - "title": "Scariness Description", - "type": "string" - }, - "scariness_score": { - "title": "Scariness Score", - "type": "integer" - }, - "task_id": { - "format": "uuid", - "title": "Task Id", - "type": "string" - } - }, - "required": [ - "executable", - "crash_type", - "crash_site", - "call_stack", - "call_stack_sha256", - "input_sha256", - "task_id", - "job_id" - ], - "title": "Report", - "type": "object" - }, - "StatsFormat": { - "description": "An enumeration.", - "enum": [ - "AFL" - ], - "title": "StatsFormat" - }, - "TaskConfig": { - "properties": { - "colocate": { - "title": "Colocate", - "type": "boolean" - }, - "containers": { - "items": { - "$ref": "#/definitions/TaskContainers" - }, - "title": "Containers", - "type": "array" - }, - "debug": { - "items": { - "$ref": "#/definitions/TaskDebugFlag" - }, - "type": "array" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "pool": { - "$ref": "#/definitions/TaskPool" - }, - "prereq_tasks": { - "items": { - "format": "uuid", - "type": "string" - }, - "title": "Prereq Tasks", - "type": "array" - }, - "tags": { - "additionalProperties": { - "type": "string" - }, - "title": "Tags", - "type": "object" - }, - "task": { - "$ref": "#/definitions/TaskDetails" - }, - "vm": { - "$ref": "#/definitions/TaskVm" - } - }, - "required": [ - "job_id", - "task", - "containers", - "tags" - ], - "title": "TaskConfig", - "type": "object" - }, - "TaskContainers": { - "properties": { - "name": { - "title": "Name", - "type": "string" - }, - "type": { - "$ref": "#/definitions/ContainerType" - } - }, - "required": [ - "type", - "name" - ], - "title": "TaskContainers", - "type": "object" - }, - "TaskDebugFlag": { - "description": "An enumeration.", - "enum": [ - "keep_node_on_failure", - "keep_node_on_completion" - ], - "title": "TaskDebugFlag" - }, - "TaskDetails": { - "properties": { - "analyzer_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Analyzer Env", - "type": "object" - }, - "analyzer_exe": { - "title": "Analyzer Exe", - "type": "string" - }, - "analyzer_options": { - "items": { - "type": "string" - }, - "title": "Analyzer Options", - "type": "array" - }, - "check_asan_log": { - "title": "Check Asan Log", - "type": "boolean" - }, - "check_debugger": { - "default": true, - "title": "Check Debugger", - "type": "boolean" - }, - "check_fuzzer_help": { - "title": "Check Fuzzer Help", - "type": "boolean" - }, - "check_retry_count": { - "title": "Check Retry Count", - "type": "integer" - }, - "duration": { - "title": "Duration", - "type": "integer" - }, - "ensemble_sync_delay": { - "title": "Ensemble Sync Delay", - "type": "integer" - }, - "expect_crash_on_failure": { - "title": "Expect Crash On Failure", - "type": "boolean" - }, - "generator_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Generator Env", - "type": "object" - }, - "generator_exe": { - "title": "Generator Exe", - "type": "string" - }, - "generator_options": { - "items": { - "type": "string" - }, - "title": "Generator Options", - "type": "array" - }, - "minimized_stack_depth": { - "title": "Minimized Stack Depth", - "type": "integer" - }, - "preserve_existing_outputs": { - "title": "Preserve Existing Outputs", - "type": "boolean" - }, - "reboot_after_setup": { - "title": "Reboot After Setup", - "type": "boolean" - }, - "rename_output": { - "title": "Rename Output", - "type": "boolean" - }, - "report_list": { - "items": { - "type": "string" - }, - "title": "Report List", - "type": "array" - }, - "stats_file": { - "title": "Stats File", - "type": "string" - }, - "stats_format": { - "$ref": "#/definitions/StatsFormat" - }, - "supervisor_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Supervisor Env", - "type": "object" - }, - "supervisor_exe": { - "title": "Supervisor Exe", - "type": "string" - }, - "supervisor_input_marker": { - "title": "Supervisor Input Marker", - "type": "string" - }, - "supervisor_options": { - "items": { - "type": "string" - }, - "title": "Supervisor Options", - "type": "array" - }, - "target_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Target Env", - "type": "object" - }, - "target_exe": { - "title": "Target Exe", - "type": "string" - }, - "target_options": { - "items": { - "type": "string" - }, - "title": "Target Options", - "type": "array" - }, - "target_options_merge": { - "title": "Target Options Merge", - "type": "boolean" - }, - "target_timeout": { - "title": "Target Timeout", - "type": "integer" - }, - "target_workers": { - "title": "Target Workers", - "type": "integer" - }, - "type": { - "$ref": "#/definitions/TaskType" - }, - "wait_for_files": { - "$ref": "#/definitions/ContainerType" - } - }, - "required": [ - "type", - "duration" - ], - "title": "TaskDetails", - "type": "object" - }, - "TaskPool": { - "properties": { - "count": { - "title": "Count", - "type": "integer" - }, - "pool_name": { - "title": "Pool Name", - "type": "string" - } - }, - "required": [ - "count", - "pool_name" - ], - "title": "TaskPool", - "type": "object" - }, - "TaskType": { - "description": "An enumeration.", - "enum": [ - "libfuzzer_fuzz", - "libfuzzer_coverage", - "libfuzzer_crash_report", - "libfuzzer_merge", - "libfuzzer_regression", - "generic_analysis", - "generic_supervisor", - "generic_merge", - "generic_generator", - "generic_crash_report", - "generic_regression" - ], - "title": "TaskType" - }, - "TaskVm": { - "properties": { - "count": { - "default": 1, - "title": "Count", - "type": "integer" - }, - "image": { - "title": "Image", - "type": "string" - }, - "reboot_after_setup": { - "title": "Reboot After Setup", - "type": "boolean" - }, - "region": { - "title": "Region", - "type": "string" - }, - "sku": { - "title": "Sku", - "type": "string" - }, - "spot_instances": { - "default": false, - "title": "Spot Instances", - "type": "boolean" - } - }, - "required": [ - "region", - "sku", - "image" - ], - "title": "TaskVm", - "type": "object" - } - }, - "properties": { - "container": { - "title": "Container", - "type": "string" - }, - "filename": { - "title": "Filename", - "type": "string" - }, - "regression_report": { - "$ref": "#/definitions/RegressionReport" - }, - "task_config": { - "$ref": "#/definitions/TaskConfig" - } - }, - "required": [ - "regression_report", - "container", - "filename" - ], - "title": "EventRegressionReported", - "type": "object" -} -``` - -### scaleset_created - -#### Example - -```json -{ - "image": "Canonical:UbuntuServer:18.04-LTS:latest", - "pool_name": "example", - "region": "eastus", - "scaleset_id": "00000000-0000-0000-0000-000000000000", - "size": 10, - "vm_sku": "Standard_D2s_v3" -} -``` - -#### Schema - -```json -{ - "properties": { - "image": { - "title": "Image", - "type": "string" - }, - "pool_name": { - "title": "Pool Name", - "type": "string" - }, - "region": { - "title": "Region", - "type": "string" - }, - "scaleset_id": { - "format": "uuid", - "title": "Scaleset Id", - "type": "string" - }, - "size": { - "title": "Size", - "type": "integer" - }, - "vm_sku": { - "title": "Vm Sku", - "type": "string" - } - }, - "required": [ - "scaleset_id", - "pool_name", - "vm_sku", - "image", - "region", - "size" - ], - "title": "EventScalesetCreated", - "type": "object" -} -``` - -### scaleset_deleted - -#### Example - -```json -{ - "pool_name": "example", - "scaleset_id": "00000000-0000-0000-0000-000000000000" -} -``` - -#### Schema - -```json -{ - "properties": { - "pool_name": { - "title": "Pool Name", - "type": "string" - }, - "scaleset_id": { - "format": "uuid", - "title": "Scaleset Id", - "type": "string" - } - }, - "required": [ - "scaleset_id", - "pool_name" - ], - "title": "EventScalesetDeleted", - "type": "object" -} -``` - -### scaleset_failed - -#### Example - -```json -{ - "error": { - "code": 456, - "errors": [ - "example error message" - ] - }, - "pool_name": "example", - "scaleset_id": "00000000-0000-0000-0000-000000000000" -} -``` - -#### Schema - -```json -{ - "definitions": { - "Error": { - "properties": { - "code": { - "$ref": "#/definitions/ErrorCode" - }, - "errors": { - "items": { - "type": "string" - }, - "title": "Errors", - "type": "array" - } - }, - "required": [ - "code", - "errors" - ], - "title": "Error", - "type": "object" - }, - "ErrorCode": { - "description": "An enumeration.", - "enum": [ - 450, - 451, - 452, - 453, - 454, - 455, - 456, - 457, - 458, - 459, - 460, - 461, - 462, - 463, - 464, - 465, - 467, - 468, - 469, - 470, - 471, - 472 - ], - "title": "ErrorCode" - } - }, - "properties": { - "error": { - "$ref": "#/definitions/Error" - }, - "pool_name": { - "title": "Pool Name", - "type": "string" - }, - "scaleset_id": { - "format": "uuid", - "title": "Scaleset Id", - "type": "string" - } - }, - "required": [ - "scaleset_id", - "pool_name", - "error" - ], - "title": "EventScalesetFailed", - "type": "object" -} -``` - -### task_created - -#### Example - -```json -{ - "config": { - "containers": [ - { - "name": "my-setup", - "type": "setup" - }, - { - "name": "my-inputs", - "type": "inputs" - }, - { - "name": "my-crashes", - "type": "crashes" - } - ], - "job_id": "00000000-0000-0000-0000-000000000000", - "tags": {}, - "task": { - "check_debugger": true, - "duration": 1, - "target_env": {}, - "target_exe": "fuzz.exe", - "target_options": [], - "type": "libfuzzer_fuzz" - } - }, - "job_id": "00000000-0000-0000-0000-000000000000", - "task_id": "00000000-0000-0000-0000-000000000000", - "user_info": { - "application_id": "00000000-0000-0000-0000-000000000000", - "object_id": "00000000-0000-0000-0000-000000000000", - "upn": "example@contoso.com" - } -} -``` - -#### Schema - -```json -{ - "definitions": { - "ContainerType": { - "description": "An enumeration.", - "enum": [ - "analysis", - "coverage", - "crashes", - "inputs", - "no_repro", - "readonly_inputs", - "reports", - "setup", - "tools", - "unique_inputs", - "unique_reports", - "regression_reports" - ], - "title": "ContainerType" - }, - "StatsFormat": { - "description": "An enumeration.", - "enum": [ - "AFL" - ], - "title": "StatsFormat" - }, - "TaskConfig": { - "properties": { - "colocate": { - "title": "Colocate", - "type": "boolean" - }, - "containers": { - "items": { - "$ref": "#/definitions/TaskContainers" - }, - "title": "Containers", - "type": "array" - }, - "debug": { - "items": { - "$ref": "#/definitions/TaskDebugFlag" - }, - "type": "array" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "pool": { - "$ref": "#/definitions/TaskPool" - }, - "prereq_tasks": { - "items": { - "format": "uuid", - "type": "string" - }, - "title": "Prereq Tasks", - "type": "array" - }, - "tags": { - "additionalProperties": { - "type": "string" - }, - "title": "Tags", - "type": "object" - }, - "task": { - "$ref": "#/definitions/TaskDetails" - }, - "vm": { - "$ref": "#/definitions/TaskVm" - } - }, - "required": [ - "job_id", - "task", - "containers", - "tags" - ], - "title": "TaskConfig", - "type": "object" - }, - "TaskContainers": { - "properties": { - "name": { - "title": "Name", - "type": "string" - }, - "type": { - "$ref": "#/definitions/ContainerType" - } - }, - "required": [ - "type", - "name" - ], - "title": "TaskContainers", - "type": "object" - }, - "TaskDebugFlag": { - "description": "An enumeration.", - "enum": [ - "keep_node_on_failure", - "keep_node_on_completion" - ], - "title": "TaskDebugFlag" - }, - "TaskDetails": { - "properties": { - "analyzer_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Analyzer Env", - "type": "object" - }, - "analyzer_exe": { - "title": "Analyzer Exe", - "type": "string" - }, - "analyzer_options": { - "items": { - "type": "string" - }, - "title": "Analyzer Options", - "type": "array" - }, - "check_asan_log": { - "title": "Check Asan Log", - "type": "boolean" - }, - "check_debugger": { - "default": true, - "title": "Check Debugger", - "type": "boolean" - }, - "check_fuzzer_help": { - "title": "Check Fuzzer Help", - "type": "boolean" - }, - "check_retry_count": { - "title": "Check Retry Count", - "type": "integer" - }, - "duration": { - "title": "Duration", - "type": "integer" - }, - "ensemble_sync_delay": { - "title": "Ensemble Sync Delay", - "type": "integer" - }, - "expect_crash_on_failure": { - "title": "Expect Crash On Failure", - "type": "boolean" - }, - "generator_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Generator Env", - "type": "object" - }, - "generator_exe": { - "title": "Generator Exe", - "type": "string" - }, - "generator_options": { - "items": { - "type": "string" - }, - "title": "Generator Options", - "type": "array" - }, - "minimized_stack_depth": { - "title": "Minimized Stack Depth", - "type": "integer" - }, - "preserve_existing_outputs": { - "title": "Preserve Existing Outputs", - "type": "boolean" - }, - "reboot_after_setup": { - "title": "Reboot After Setup", - "type": "boolean" - }, - "rename_output": { - "title": "Rename Output", - "type": "boolean" - }, - "report_list": { - "items": { - "type": "string" - }, - "title": "Report List", - "type": "array" - }, - "stats_file": { - "title": "Stats File", - "type": "string" - }, - "stats_format": { - "$ref": "#/definitions/StatsFormat" - }, - "supervisor_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Supervisor Env", - "type": "object" - }, - "supervisor_exe": { - "title": "Supervisor Exe", - "type": "string" - }, - "supervisor_input_marker": { - "title": "Supervisor Input Marker", - "type": "string" - }, - "supervisor_options": { - "items": { - "type": "string" - }, - "title": "Supervisor Options", - "type": "array" - }, - "target_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Target Env", - "type": "object" - }, - "target_exe": { - "title": "Target Exe", - "type": "string" - }, - "target_options": { - "items": { - "type": "string" - }, - "title": "Target Options", - "type": "array" - }, - "target_options_merge": { - "title": "Target Options Merge", - "type": "boolean" - }, - "target_timeout": { - "title": "Target Timeout", - "type": "integer" - }, - "target_workers": { - "title": "Target Workers", - "type": "integer" - }, - "type": { - "$ref": "#/definitions/TaskType" - }, - "wait_for_files": { - "$ref": "#/definitions/ContainerType" - } - }, - "required": [ - "type", - "duration" - ], - "title": "TaskDetails", - "type": "object" - }, - "TaskPool": { - "properties": { - "count": { - "title": "Count", - "type": "integer" - }, - "pool_name": { - "title": "Pool Name", - "type": "string" - } - }, - "required": [ - "count", - "pool_name" - ], - "title": "TaskPool", - "type": "object" - }, - "TaskType": { - "description": "An enumeration.", - "enum": [ - "libfuzzer_fuzz", - "libfuzzer_coverage", - "libfuzzer_crash_report", - "libfuzzer_merge", - "libfuzzer_regression", - "generic_analysis", - "generic_supervisor", - "generic_merge", - "generic_generator", - "generic_crash_report", - "generic_regression" - ], - "title": "TaskType" - }, - "TaskVm": { - "properties": { - "count": { - "default": 1, - "title": "Count", - "type": "integer" - }, - "image": { - "title": "Image", - "type": "string" - }, - "reboot_after_setup": { - "title": "Reboot After Setup", - "type": "boolean" - }, - "region": { - "title": "Region", - "type": "string" - }, - "sku": { - "title": "Sku", - "type": "string" - }, - "spot_instances": { - "default": false, - "title": "Spot Instances", - "type": "boolean" - } - }, - "required": [ - "region", - "sku", - "image" - ], - "title": "TaskVm", - "type": "object" - }, - "UserInfo": { - "properties": { - "application_id": { - "format": "uuid", - "title": "Application Id", - "type": "string" - }, - "object_id": { - "format": "uuid", - "title": "Object Id", - "type": "string" - }, - "upn": { - "title": "Upn", - "type": "string" - } - }, - "title": "UserInfo", - "type": "object" - } - }, - "properties": { - "config": { - "$ref": "#/definitions/TaskConfig" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "task_id": { - "format": "uuid", - "title": "Task Id", - "type": "string" - }, - "user_info": { - "$ref": "#/definitions/UserInfo" - } - }, - "required": [ - "job_id", - "task_id", - "config" - ], - "title": "EventTaskCreated", - "type": "object" -} -``` - -### task_failed - -#### Example - -```json -{ - "config": { - "containers": [ - { - "name": "my-setup", - "type": "setup" - }, - { - "name": "my-inputs", - "type": "inputs" - }, - { - "name": "my-crashes", - "type": "crashes" - } - ], - "job_id": "00000000-0000-0000-0000-000000000000", - "tags": {}, - "task": { - "check_debugger": true, - "duration": 1, - "target_env": {}, - "target_exe": "fuzz.exe", - "target_options": [], - "type": "libfuzzer_fuzz" - } - }, - "error": { - "code": 468, - "errors": [ - "example error message" - ] - }, - "job_id": "00000000-0000-0000-0000-000000000000", - "task_id": "00000000-0000-0000-0000-000000000000", - "user_info": { - "application_id": "00000000-0000-0000-0000-000000000000", - "object_id": "00000000-0000-0000-0000-000000000000", - "upn": "example@contoso.com" - } -} -``` - -#### Schema - -```json -{ - "definitions": { - "ContainerType": { - "description": "An enumeration.", - "enum": [ - "analysis", - "coverage", - "crashes", - "inputs", - "no_repro", - "readonly_inputs", - "reports", - "setup", - "tools", - "unique_inputs", - "unique_reports", - "regression_reports" - ], - "title": "ContainerType" - }, - "Error": { - "properties": { - "code": { - "$ref": "#/definitions/ErrorCode" - }, - "errors": { - "items": { - "type": "string" - }, - "title": "Errors", - "type": "array" - } - }, - "required": [ - "code", - "errors" - ], - "title": "Error", - "type": "object" - }, - "ErrorCode": { - "description": "An enumeration.", - "enum": [ - 450, - 451, - 452, - 453, - 454, - 455, - 456, - 457, - 458, - 459, - 460, - 461, - 462, - 463, - 464, - 465, - 467, - 468, - 469, - 470, - 471, - 472 - ], - "title": "ErrorCode" - }, - "StatsFormat": { - "description": "An enumeration.", - "enum": [ - "AFL" - ], - "title": "StatsFormat" - }, - "TaskConfig": { - "properties": { - "colocate": { - "title": "Colocate", - "type": "boolean" - }, - "containers": { - "items": { - "$ref": "#/definitions/TaskContainers" - }, - "title": "Containers", - "type": "array" - }, - "debug": { - "items": { - "$ref": "#/definitions/TaskDebugFlag" - }, - "type": "array" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "pool": { - "$ref": "#/definitions/TaskPool" - }, - "prereq_tasks": { - "items": { - "format": "uuid", - "type": "string" - }, - "title": "Prereq Tasks", - "type": "array" - }, - "tags": { - "additionalProperties": { - "type": "string" - }, - "title": "Tags", - "type": "object" - }, - "task": { - "$ref": "#/definitions/TaskDetails" - }, - "vm": { - "$ref": "#/definitions/TaskVm" - } - }, - "required": [ - "job_id", - "task", - "containers", - "tags" - ], - "title": "TaskConfig", - "type": "object" - }, - "TaskContainers": { - "properties": { - "name": { - "title": "Name", - "type": "string" - }, - "type": { - "$ref": "#/definitions/ContainerType" - } - }, - "required": [ - "type", - "name" - ], - "title": "TaskContainers", - "type": "object" - }, - "TaskDebugFlag": { - "description": "An enumeration.", - "enum": [ - "keep_node_on_failure", - "keep_node_on_completion" - ], - "title": "TaskDebugFlag" - }, - "TaskDetails": { - "properties": { - "analyzer_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Analyzer Env", - "type": "object" - }, - "analyzer_exe": { - "title": "Analyzer Exe", - "type": "string" - }, - "analyzer_options": { - "items": { - "type": "string" - }, - "title": "Analyzer Options", - "type": "array" - }, - "check_asan_log": { - "title": "Check Asan Log", - "type": "boolean" - }, - "check_debugger": { - "default": true, - "title": "Check Debugger", - "type": "boolean" - }, - "check_fuzzer_help": { - "title": "Check Fuzzer Help", - "type": "boolean" - }, - "check_retry_count": { - "title": "Check Retry Count", - "type": "integer" - }, - "duration": { - "title": "Duration", - "type": "integer" - }, - "ensemble_sync_delay": { - "title": "Ensemble Sync Delay", - "type": "integer" - }, - "expect_crash_on_failure": { - "title": "Expect Crash On Failure", - "type": "boolean" - }, - "generator_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Generator Env", - "type": "object" - }, - "generator_exe": { - "title": "Generator Exe", - "type": "string" - }, - "generator_options": { - "items": { - "type": "string" - }, - "title": "Generator Options", - "type": "array" - }, - "minimized_stack_depth": { - "title": "Minimized Stack Depth", - "type": "integer" - }, - "preserve_existing_outputs": { - "title": "Preserve Existing Outputs", - "type": "boolean" - }, - "reboot_after_setup": { - "title": "Reboot After Setup", - "type": "boolean" - }, - "rename_output": { - "title": "Rename Output", - "type": "boolean" - }, - "report_list": { - "items": { - "type": "string" - }, - "title": "Report List", - "type": "array" - }, - "stats_file": { - "title": "Stats File", - "type": "string" - }, - "stats_format": { - "$ref": "#/definitions/StatsFormat" - }, - "supervisor_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Supervisor Env", - "type": "object" - }, - "supervisor_exe": { - "title": "Supervisor Exe", - "type": "string" - }, - "supervisor_input_marker": { - "title": "Supervisor Input Marker", - "type": "string" - }, - "supervisor_options": { - "items": { - "type": "string" - }, - "title": "Supervisor Options", - "type": "array" - }, - "target_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Target Env", - "type": "object" - }, - "target_exe": { - "title": "Target Exe", - "type": "string" - }, - "target_options": { - "items": { - "type": "string" - }, - "title": "Target Options", - "type": "array" - }, - "target_options_merge": { - "title": "Target Options Merge", - "type": "boolean" - }, - "target_timeout": { - "title": "Target Timeout", - "type": "integer" - }, - "target_workers": { - "title": "Target Workers", - "type": "integer" - }, - "type": { - "$ref": "#/definitions/TaskType" - }, - "wait_for_files": { - "$ref": "#/definitions/ContainerType" - } - }, - "required": [ - "type", - "duration" - ], - "title": "TaskDetails", - "type": "object" - }, - "TaskPool": { - "properties": { - "count": { - "title": "Count", - "type": "integer" - }, - "pool_name": { - "title": "Pool Name", - "type": "string" - } - }, - "required": [ - "count", - "pool_name" - ], - "title": "TaskPool", - "type": "object" - }, - "TaskType": { - "description": "An enumeration.", - "enum": [ - "libfuzzer_fuzz", - "libfuzzer_coverage", - "libfuzzer_crash_report", - "libfuzzer_merge", - "libfuzzer_regression", - "generic_analysis", - "generic_supervisor", - "generic_merge", - "generic_generator", - "generic_crash_report", - "generic_regression" - ], - "title": "TaskType" - }, - "TaskVm": { - "properties": { - "count": { - "default": 1, - "title": "Count", - "type": "integer" - }, - "image": { - "title": "Image", - "type": "string" - }, - "reboot_after_setup": { - "title": "Reboot After Setup", - "type": "boolean" - }, - "region": { - "title": "Region", - "type": "string" - }, - "sku": { - "title": "Sku", - "type": "string" - }, - "spot_instances": { - "default": false, - "title": "Spot Instances", - "type": "boolean" - } - }, - "required": [ - "region", - "sku", - "image" - ], - "title": "TaskVm", - "type": "object" - }, - "UserInfo": { - "properties": { - "application_id": { - "format": "uuid", - "title": "Application Id", - "type": "string" - }, - "object_id": { - "format": "uuid", - "title": "Object Id", - "type": "string" - }, - "upn": { - "title": "Upn", - "type": "string" - } - }, - "title": "UserInfo", - "type": "object" - } - }, - "properties": { - "config": { - "$ref": "#/definitions/TaskConfig" - }, - "error": { - "$ref": "#/definitions/Error" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "task_id": { - "format": "uuid", - "title": "Task Id", - "type": "string" - }, - "user_info": { - "$ref": "#/definitions/UserInfo" - } - }, - "required": [ - "job_id", - "task_id", - "error", - "config" - ], - "title": "EventTaskFailed", - "type": "object" -} -``` - -### task_heartbeat - -#### Example - -```json -{ - "config": { - "containers": [ - { - "name": "my-setup", - "type": "setup" - }, - { - "name": "my-inputs", - "type": "inputs" - }, - { - "name": "my-crashes", - "type": "crashes" - } - ], - "job_id": "00000000-0000-0000-0000-000000000000", - "tags": {}, - "task": { - "check_debugger": true, - "duration": 1, - "target_env": {}, - "target_exe": "fuzz.exe", - "target_options": [], - "type": "libfuzzer_fuzz" - } - }, - "job_id": "00000000-0000-0000-0000-000000000000", - "task_id": "00000000-0000-0000-0000-000000000000" -} -``` - -#### Schema - -```json -{ - "definitions": { - "ContainerType": { - "description": "An enumeration.", - "enum": [ - "analysis", - "coverage", - "crashes", - "inputs", - "no_repro", - "readonly_inputs", - "reports", - "setup", - "tools", - "unique_inputs", - "unique_reports", - "regression_reports" - ], - "title": "ContainerType" - }, - "StatsFormat": { - "description": "An enumeration.", - "enum": [ - "AFL" - ], - "title": "StatsFormat" - }, - "TaskConfig": { - "properties": { - "colocate": { - "title": "Colocate", - "type": "boolean" - }, - "containers": { - "items": { - "$ref": "#/definitions/TaskContainers" - }, - "title": "Containers", - "type": "array" - }, - "debug": { - "items": { - "$ref": "#/definitions/TaskDebugFlag" - }, - "type": "array" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "pool": { - "$ref": "#/definitions/TaskPool" - }, - "prereq_tasks": { - "items": { - "format": "uuid", - "type": "string" - }, - "title": "Prereq Tasks", - "type": "array" - }, - "tags": { - "additionalProperties": { - "type": "string" - }, - "title": "Tags", - "type": "object" - }, - "task": { - "$ref": "#/definitions/TaskDetails" - }, - "vm": { - "$ref": "#/definitions/TaskVm" - } - }, - "required": [ - "job_id", - "task", - "containers", - "tags" - ], - "title": "TaskConfig", - "type": "object" - }, - "TaskContainers": { - "properties": { - "name": { - "title": "Name", - "type": "string" - }, - "type": { - "$ref": "#/definitions/ContainerType" - } - }, - "required": [ - "type", - "name" - ], - "title": "TaskContainers", - "type": "object" - }, - "TaskDebugFlag": { - "description": "An enumeration.", - "enum": [ - "keep_node_on_failure", - "keep_node_on_completion" - ], - "title": "TaskDebugFlag" - }, - "TaskDetails": { - "properties": { - "analyzer_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Analyzer Env", - "type": "object" - }, - "analyzer_exe": { - "title": "Analyzer Exe", - "type": "string" - }, - "analyzer_options": { - "items": { - "type": "string" - }, - "title": "Analyzer Options", - "type": "array" - }, - "check_asan_log": { - "title": "Check Asan Log", - "type": "boolean" - }, - "check_debugger": { - "default": true, - "title": "Check Debugger", - "type": "boolean" - }, - "check_fuzzer_help": { - "title": "Check Fuzzer Help", - "type": "boolean" - }, - "check_retry_count": { - "title": "Check Retry Count", - "type": "integer" - }, - "duration": { - "title": "Duration", - "type": "integer" - }, - "ensemble_sync_delay": { - "title": "Ensemble Sync Delay", - "type": "integer" - }, - "expect_crash_on_failure": { - "title": "Expect Crash On Failure", - "type": "boolean" - }, - "generator_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Generator Env", - "type": "object" - }, - "generator_exe": { - "title": "Generator Exe", - "type": "string" - }, - "generator_options": { - "items": { - "type": "string" - }, - "title": "Generator Options", - "type": "array" - }, - "minimized_stack_depth": { - "title": "Minimized Stack Depth", - "type": "integer" - }, - "preserve_existing_outputs": { - "title": "Preserve Existing Outputs", - "type": "boolean" - }, - "reboot_after_setup": { - "title": "Reboot After Setup", - "type": "boolean" - }, - "rename_output": { - "title": "Rename Output", - "type": "boolean" - }, - "report_list": { - "items": { - "type": "string" - }, - "title": "Report List", - "type": "array" - }, - "stats_file": { - "title": "Stats File", - "type": "string" - }, - "stats_format": { - "$ref": "#/definitions/StatsFormat" - }, - "supervisor_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Supervisor Env", - "type": "object" - }, - "supervisor_exe": { - "title": "Supervisor Exe", - "type": "string" - }, - "supervisor_input_marker": { - "title": "Supervisor Input Marker", - "type": "string" - }, - "supervisor_options": { - "items": { - "type": "string" - }, - "title": "Supervisor Options", - "type": "array" - }, - "target_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Target Env", - "type": "object" - }, - "target_exe": { - "title": "Target Exe", - "type": "string" - }, - "target_options": { - "items": { - "type": "string" - }, - "title": "Target Options", - "type": "array" - }, - "target_options_merge": { - "title": "Target Options Merge", - "type": "boolean" - }, - "target_timeout": { - "title": "Target Timeout", - "type": "integer" - }, - "target_workers": { - "title": "Target Workers", - "type": "integer" - }, - "type": { - "$ref": "#/definitions/TaskType" - }, - "wait_for_files": { - "$ref": "#/definitions/ContainerType" - } - }, - "required": [ - "type", - "duration" - ], - "title": "TaskDetails", - "type": "object" - }, - "TaskPool": { - "properties": { - "count": { - "title": "Count", - "type": "integer" - }, - "pool_name": { - "title": "Pool Name", - "type": "string" - } - }, - "required": [ - "count", - "pool_name" - ], - "title": "TaskPool", - "type": "object" - }, - "TaskType": { - "description": "An enumeration.", - "enum": [ - "libfuzzer_fuzz", - "libfuzzer_coverage", - "libfuzzer_crash_report", - "libfuzzer_merge", - "libfuzzer_regression", - "generic_analysis", - "generic_supervisor", - "generic_merge", - "generic_generator", - "generic_crash_report", - "generic_regression" - ], - "title": "TaskType" - }, - "TaskVm": { - "properties": { - "count": { - "default": 1, - "title": "Count", - "type": "integer" - }, - "image": { - "title": "Image", - "type": "string" - }, - "reboot_after_setup": { - "title": "Reboot After Setup", - "type": "boolean" - }, - "region": { - "title": "Region", - "type": "string" - }, - "sku": { - "title": "Sku", - "type": "string" - }, - "spot_instances": { - "default": false, - "title": "Spot Instances", - "type": "boolean" - } - }, - "required": [ - "region", - "sku", - "image" - ], - "title": "TaskVm", - "type": "object" - } - }, - "properties": { - "config": { - "$ref": "#/definitions/TaskConfig" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "task_id": { - "format": "uuid", - "title": "Task Id", - "type": "string" - } - }, - "required": [ - "job_id", - "task_id", - "config" - ], - "title": "EventTaskHeartbeat", - "type": "object" -} -``` - -### task_state_updated - -#### Example - -```json -{ - "config": { - "containers": [ - { - "name": "my-setup", - "type": "setup" - }, - { - "name": "my-inputs", - "type": "inputs" - }, - { - "name": "my-crashes", - "type": "crashes" - } - ], - "job_id": "00000000-0000-0000-0000-000000000000", - "tags": {}, - "task": { - "check_debugger": true, - "duration": 1, - "target_env": {}, - "target_exe": "fuzz.exe", - "target_options": [], - "type": "libfuzzer_fuzz" - } - }, - "job_id": "00000000-0000-0000-0000-000000000000", - "state": "init", - "task_id": "00000000-0000-0000-0000-000000000000" -} -``` - -#### Schema - -```json -{ - "definitions": { - "ContainerType": { - "description": "An enumeration.", - "enum": [ - "analysis", - "coverage", - "crashes", - "inputs", - "no_repro", - "readonly_inputs", - "reports", - "setup", - "tools", - "unique_inputs", - "unique_reports", - "regression_reports" - ], - "title": "ContainerType" - }, - "StatsFormat": { - "description": "An enumeration.", - "enum": [ - "AFL" - ], - "title": "StatsFormat" - }, - "TaskConfig": { - "properties": { - "colocate": { - "title": "Colocate", - "type": "boolean" - }, - "containers": { - "items": { - "$ref": "#/definitions/TaskContainers" - }, - "title": "Containers", - "type": "array" - }, - "debug": { - "items": { - "$ref": "#/definitions/TaskDebugFlag" - }, - "type": "array" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "pool": { - "$ref": "#/definitions/TaskPool" - }, - "prereq_tasks": { - "items": { - "format": "uuid", - "type": "string" - }, - "title": "Prereq Tasks", - "type": "array" - }, - "tags": { - "additionalProperties": { - "type": "string" - }, - "title": "Tags", - "type": "object" - }, - "task": { - "$ref": "#/definitions/TaskDetails" - }, - "vm": { - "$ref": "#/definitions/TaskVm" - } - }, - "required": [ - "job_id", - "task", - "containers", - "tags" - ], - "title": "TaskConfig", - "type": "object" - }, - "TaskContainers": { - "properties": { - "name": { - "title": "Name", - "type": "string" - }, - "type": { - "$ref": "#/definitions/ContainerType" - } - }, - "required": [ - "type", - "name" - ], - "title": "TaskContainers", - "type": "object" - }, - "TaskDebugFlag": { - "description": "An enumeration.", - "enum": [ - "keep_node_on_failure", - "keep_node_on_completion" - ], - "title": "TaskDebugFlag" - }, - "TaskDetails": { - "properties": { - "analyzer_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Analyzer Env", - "type": "object" - }, - "analyzer_exe": { - "title": "Analyzer Exe", - "type": "string" - }, - "analyzer_options": { - "items": { - "type": "string" - }, - "title": "Analyzer Options", - "type": "array" - }, - "check_asan_log": { - "title": "Check Asan Log", - "type": "boolean" - }, - "check_debugger": { - "default": true, - "title": "Check Debugger", - "type": "boolean" - }, - "check_fuzzer_help": { - "title": "Check Fuzzer Help", - "type": "boolean" - }, - "check_retry_count": { - "title": "Check Retry Count", - "type": "integer" - }, - "duration": { - "title": "Duration", - "type": "integer" - }, - "ensemble_sync_delay": { - "title": "Ensemble Sync Delay", - "type": "integer" - }, - "expect_crash_on_failure": { - "title": "Expect Crash On Failure", - "type": "boolean" - }, - "generator_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Generator Env", - "type": "object" - }, - "generator_exe": { - "title": "Generator Exe", - "type": "string" - }, - "generator_options": { - "items": { - "type": "string" - }, - "title": "Generator Options", - "type": "array" - }, - "minimized_stack_depth": { - "title": "Minimized Stack Depth", - "type": "integer" - }, - "preserve_existing_outputs": { - "title": "Preserve Existing Outputs", - "type": "boolean" - }, - "reboot_after_setup": { - "title": "Reboot After Setup", - "type": "boolean" - }, - "rename_output": { - "title": "Rename Output", - "type": "boolean" - }, - "report_list": { - "items": { - "type": "string" - }, - "title": "Report List", - "type": "array" - }, - "stats_file": { - "title": "Stats File", - "type": "string" - }, - "stats_format": { - "$ref": "#/definitions/StatsFormat" - }, - "supervisor_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Supervisor Env", - "type": "object" - }, - "supervisor_exe": { - "title": "Supervisor Exe", - "type": "string" - }, - "supervisor_input_marker": { - "title": "Supervisor Input Marker", - "type": "string" - }, - "supervisor_options": { - "items": { - "type": "string" - }, - "title": "Supervisor Options", - "type": "array" - }, - "target_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Target Env", - "type": "object" - }, - "target_exe": { - "title": "Target Exe", - "type": "string" - }, - "target_options": { - "items": { - "type": "string" - }, - "title": "Target Options", - "type": "array" - }, - "target_options_merge": { - "title": "Target Options Merge", - "type": "boolean" - }, - "target_timeout": { - "title": "Target Timeout", - "type": "integer" - }, - "target_workers": { - "title": "Target Workers", - "type": "integer" - }, - "type": { - "$ref": "#/definitions/TaskType" - }, - "wait_for_files": { - "$ref": "#/definitions/ContainerType" - } - }, - "required": [ - "type", - "duration" - ], - "title": "TaskDetails", - "type": "object" - }, - "TaskPool": { - "properties": { - "count": { - "title": "Count", - "type": "integer" - }, - "pool_name": { - "title": "Pool Name", - "type": "string" - } - }, - "required": [ - "count", - "pool_name" - ], - "title": "TaskPool", - "type": "object" - }, - "TaskState": { - "description": "An enumeration.", - "enum": [ - "init", - "waiting", - "scheduled", - "setting_up", - "running", - "stopping", - "stopped", - "wait_job" - ], - "title": "TaskState" - }, - "TaskType": { - "description": "An enumeration.", - "enum": [ - "libfuzzer_fuzz", - "libfuzzer_coverage", - "libfuzzer_crash_report", - "libfuzzer_merge", - "libfuzzer_regression", - "generic_analysis", - "generic_supervisor", - "generic_merge", - "generic_generator", - "generic_crash_report", - "generic_regression" - ], - "title": "TaskType" - }, - "TaskVm": { - "properties": { - "count": { - "default": 1, - "title": "Count", - "type": "integer" - }, - "image": { - "title": "Image", - "type": "string" - }, - "reboot_after_setup": { - "title": "Reboot After Setup", - "type": "boolean" - }, - "region": { - "title": "Region", - "type": "string" - }, - "sku": { - "title": "Sku", - "type": "string" - }, - "spot_instances": { - "default": false, - "title": "Spot Instances", - "type": "boolean" - } - }, - "required": [ - "region", - "sku", - "image" - ], - "title": "TaskVm", - "type": "object" - } - }, - "properties": { - "config": { - "$ref": "#/definitions/TaskConfig" - }, - "end_time": { - "format": "date-time", - "title": "End Time", - "type": "string" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "state": { - "$ref": "#/definitions/TaskState" - }, - "task_id": { - "format": "uuid", - "title": "Task Id", - "type": "string" - } - }, - "required": [ - "job_id", - "task_id", - "state", - "config" - ], - "title": "EventTaskStateUpdated", - "type": "object" -} -``` - -### task_stopped - -#### Example - -```json -{ - "config": { - "containers": [ - { - "name": "my-setup", - "type": "setup" - }, - { - "name": "my-inputs", - "type": "inputs" - }, - { - "name": "my-crashes", - "type": "crashes" - } - ], - "job_id": "00000000-0000-0000-0000-000000000000", - "tags": {}, - "task": { - "check_debugger": true, - "duration": 1, - "target_env": {}, - "target_exe": "fuzz.exe", - "target_options": [], - "type": "libfuzzer_fuzz" - } - }, - "job_id": "00000000-0000-0000-0000-000000000000", - "task_id": "00000000-0000-0000-0000-000000000000", - "user_info": { - "application_id": "00000000-0000-0000-0000-000000000000", - "object_id": "00000000-0000-0000-0000-000000000000", - "upn": "example@contoso.com" - } -} -``` - -#### Schema - -```json -{ - "definitions": { - "ContainerType": { - "description": "An enumeration.", - "enum": [ - "analysis", - "coverage", - "crashes", - "inputs", - "no_repro", - "readonly_inputs", - "reports", - "setup", - "tools", - "unique_inputs", - "unique_reports", - "regression_reports" - ], - "title": "ContainerType" - }, - "StatsFormat": { - "description": "An enumeration.", - "enum": [ - "AFL" - ], - "title": "StatsFormat" - }, - "TaskConfig": { - "properties": { - "colocate": { - "title": "Colocate", - "type": "boolean" - }, - "containers": { - "items": { - "$ref": "#/definitions/TaskContainers" - }, - "title": "Containers", - "type": "array" - }, - "debug": { - "items": { - "$ref": "#/definitions/TaskDebugFlag" - }, - "type": "array" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "pool": { - "$ref": "#/definitions/TaskPool" - }, - "prereq_tasks": { - "items": { - "format": "uuid", - "type": "string" - }, - "title": "Prereq Tasks", - "type": "array" - }, - "tags": { - "additionalProperties": { - "type": "string" - }, - "title": "Tags", - "type": "object" - }, - "task": { - "$ref": "#/definitions/TaskDetails" - }, - "vm": { - "$ref": "#/definitions/TaskVm" - } - }, - "required": [ - "job_id", - "task", - "containers", - "tags" - ], - "title": "TaskConfig", - "type": "object" - }, - "TaskContainers": { - "properties": { - "name": { - "title": "Name", - "type": "string" - }, - "type": { - "$ref": "#/definitions/ContainerType" - } - }, - "required": [ - "type", - "name" - ], - "title": "TaskContainers", - "type": "object" - }, - "TaskDebugFlag": { - "description": "An enumeration.", - "enum": [ - "keep_node_on_failure", - "keep_node_on_completion" - ], - "title": "TaskDebugFlag" - }, - "TaskDetails": { - "properties": { - "analyzer_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Analyzer Env", - "type": "object" - }, - "analyzer_exe": { - "title": "Analyzer Exe", - "type": "string" - }, - "analyzer_options": { - "items": { - "type": "string" - }, - "title": "Analyzer Options", - "type": "array" - }, - "check_asan_log": { - "title": "Check Asan Log", - "type": "boolean" - }, - "check_debugger": { - "default": true, - "title": "Check Debugger", - "type": "boolean" - }, - "check_fuzzer_help": { - "title": "Check Fuzzer Help", - "type": "boolean" - }, - "check_retry_count": { - "title": "Check Retry Count", - "type": "integer" - }, - "duration": { - "title": "Duration", - "type": "integer" - }, - "ensemble_sync_delay": { - "title": "Ensemble Sync Delay", - "type": "integer" - }, - "expect_crash_on_failure": { - "title": "Expect Crash On Failure", - "type": "boolean" - }, - "generator_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Generator Env", - "type": "object" - }, - "generator_exe": { - "title": "Generator Exe", - "type": "string" - }, - "generator_options": { - "items": { - "type": "string" - }, - "title": "Generator Options", - "type": "array" - }, - "minimized_stack_depth": { - "title": "Minimized Stack Depth", - "type": "integer" - }, - "preserve_existing_outputs": { - "title": "Preserve Existing Outputs", - "type": "boolean" - }, - "reboot_after_setup": { - "title": "Reboot After Setup", - "type": "boolean" - }, - "rename_output": { - "title": "Rename Output", - "type": "boolean" - }, - "report_list": { - "items": { - "type": "string" - }, - "title": "Report List", - "type": "array" - }, - "stats_file": { - "title": "Stats File", - "type": "string" - }, - "stats_format": { - "$ref": "#/definitions/StatsFormat" - }, - "supervisor_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Supervisor Env", - "type": "object" - }, - "supervisor_exe": { - "title": "Supervisor Exe", - "type": "string" - }, - "supervisor_input_marker": { - "title": "Supervisor Input Marker", - "type": "string" - }, - "supervisor_options": { - "items": { - "type": "string" - }, - "title": "Supervisor Options", - "type": "array" - }, - "target_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Target Env", - "type": "object" - }, - "target_exe": { - "title": "Target Exe", - "type": "string" - }, - "target_options": { - "items": { - "type": "string" - }, - "title": "Target Options", - "type": "array" - }, - "target_options_merge": { - "title": "Target Options Merge", - "type": "boolean" - }, - "target_timeout": { - "title": "Target Timeout", - "type": "integer" - }, - "target_workers": { - "title": "Target Workers", - "type": "integer" - }, - "type": { - "$ref": "#/definitions/TaskType" - }, - "wait_for_files": { - "$ref": "#/definitions/ContainerType" - } - }, - "required": [ - "type", - "duration" - ], - "title": "TaskDetails", - "type": "object" - }, - "TaskPool": { - "properties": { - "count": { - "title": "Count", - "type": "integer" - }, - "pool_name": { - "title": "Pool Name", - "type": "string" - } - }, - "required": [ - "count", - "pool_name" - ], - "title": "TaskPool", - "type": "object" - }, - "TaskType": { - "description": "An enumeration.", - "enum": [ - "libfuzzer_fuzz", - "libfuzzer_coverage", - "libfuzzer_crash_report", - "libfuzzer_merge", - "libfuzzer_regression", - "generic_analysis", - "generic_supervisor", - "generic_merge", - "generic_generator", - "generic_crash_report", - "generic_regression" - ], - "title": "TaskType" - }, - "TaskVm": { - "properties": { - "count": { - "default": 1, - "title": "Count", - "type": "integer" - }, - "image": { - "title": "Image", - "type": "string" - }, - "reboot_after_setup": { - "title": "Reboot After Setup", - "type": "boolean" - }, - "region": { - "title": "Region", - "type": "string" - }, - "sku": { - "title": "Sku", - "type": "string" - }, - "spot_instances": { - "default": false, - "title": "Spot Instances", - "type": "boolean" - } - }, - "required": [ - "region", - "sku", - "image" - ], - "title": "TaskVm", - "type": "object" - }, - "UserInfo": { - "properties": { - "application_id": { - "format": "uuid", - "title": "Application Id", - "type": "string" - }, - "object_id": { - "format": "uuid", - "title": "Object Id", - "type": "string" - }, - "upn": { - "title": "Upn", - "type": "string" - } - }, - "title": "UserInfo", - "type": "object" - } - }, - "properties": { - "config": { - "$ref": "#/definitions/TaskConfig" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "task_id": { - "format": "uuid", - "title": "Task Id", - "type": "string" - }, - "user_info": { - "$ref": "#/definitions/UserInfo" - } - }, - "required": [ - "job_id", - "task_id", - "config" - ], - "title": "EventTaskStopped", - "type": "object" -} -``` - -## Full Event Schema - -```json -{ - "definitions": { - "Architecture": { - "description": "An enumeration.", - "enum": [ - "x86_64" - ], - "title": "Architecture" - }, - "AutoScaleConfig": { - "properties": { - "ephemeral_os_disks": { - "default": false, - "title": "Ephemeral Os Disks", - "type": "boolean" - }, - "image": { - "title": "Image", - "type": "string" - }, - "max_size": { - "title": "Max Size", - "type": "integer" - }, - "min_size": { - "default": 0, - "title": "Min Size", - "type": "integer" - }, - "region": { - "title": "Region", - "type": "string" - }, - "scaleset_size": { - "title": "Scaleset Size", - "type": "integer" - }, - "spot_instances": { - "default": false, - "title": "Spot Instances", - "type": "boolean" - }, - "vm_sku": { - "title": "Vm Sku", - "type": "string" - } - }, - "required": [ - "image", - "scaleset_size", - "vm_sku" - ], - "title": "AutoScaleConfig", - "type": "object" - }, - "BlobRef": { - "properties": { - "account": { - "title": "Account", - "type": "string" - }, - "container": { - "title": "Container", - "type": "string" - }, - "name": { - "title": "Name", - "type": "string" - } - }, - "required": [ - "account", - "container", - "name" - ], - "title": "BlobRef", - "type": "object" - }, - "ContainerType": { - "description": "An enumeration.", - "enum": [ - "analysis", - "coverage", - "crashes", - "inputs", - "no_repro", - "readonly_inputs", - "reports", - "setup", - "tools", - "unique_inputs", - "unique_reports", - "regression_reports" - ], - "title": "ContainerType" - }, - "CrashTestResult": { - "properties": { - "crash_report": { - "$ref": "#/definitions/Report" - }, - "no_repro": { - "$ref": "#/definitions/NoReproReport" - } - }, - "title": "CrashTestResult", - "type": "object" - }, - "Error": { - "properties": { - "code": { - "$ref": "#/definitions/ErrorCode" - }, - "errors": { - "items": { - "type": "string" - }, - "title": "Errors", - "type": "array" - } - }, - "required": [ - "code", - "errors" - ], - "title": "Error", - "type": "object" - }, - "ErrorCode": { - "description": "An enumeration.", - "enum": [ - 450, - 451, - 452, - 453, - 454, - 455, - 456, - 457, - 458, - 459, - 460, - 461, - 462, - 463, - 464, - 465, - 467, - 468, - 469, - 470, - 471, - 472 - ], - "title": "ErrorCode" - }, - "EventCrashReported": { - "properties": { - "container": { - "title": "Container", - "type": "string" - }, - "filename": { - "title": "Filename", - "type": "string" - }, - "report": { - "$ref": "#/definitions/Report" - }, - "task_config": { - "$ref": "#/definitions/TaskConfig" - } - }, - "required": [ - "report", - "container", - "filename" - ], - "title": "EventCrashReported", - "type": "object" - }, - "EventFileAdded": { - "properties": { - "container": { - "title": "Container", - "type": "string" - }, - "filename": { - "title": "Filename", - "type": "string" - } - }, - "required": [ - "container", - "filename" - ], - "title": "EventFileAdded", - "type": "object" - }, - "EventJobCreated": { - "properties": { - "config": { - "$ref": "#/definitions/JobConfig" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "user_info": { - "$ref": "#/definitions/UserInfo" - } - }, - "required": [ - "job_id", - "config" - ], - "title": "EventJobCreated", - "type": "object" - }, - "EventJobStopped": { - "properties": { - "config": { - "$ref": "#/definitions/JobConfig" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "task_info": { - "items": { - "$ref": "#/definitions/JobTaskStopped" - }, - "title": "Task Info", - "type": "array" - }, - "user_info": { - "$ref": "#/definitions/UserInfo" - } - }, - "required": [ - "job_id", - "config" - ], - "title": "EventJobStopped", - "type": "object" - }, - "EventNodeCreated": { - "properties": { - "machine_id": { - "format": "uuid", - "title": "Machine Id", - "type": "string" - }, - "pool_name": { - "title": "Pool Name", - "type": "string" - }, - "scaleset_id": { - "format": "uuid", - "title": "Scaleset Id", - "type": "string" - } - }, - "required": [ - "machine_id", - "pool_name" - ], - "title": "EventNodeCreated", - "type": "object" - }, - "EventNodeDeleted": { - "properties": { - "machine_id": { - "format": "uuid", - "title": "Machine Id", - "type": "string" - }, - "pool_name": { - "title": "Pool Name", - "type": "string" - }, - "scaleset_id": { - "format": "uuid", - "title": "Scaleset Id", - "type": "string" - } - }, - "required": [ - "machine_id", - "pool_name" - ], - "title": "EventNodeDeleted", - "type": "object" - }, - "EventNodeHeartbeat": { - "properties": { - "machine_id": { - "format": "uuid", - "title": "Machine Id", - "type": "string" - }, - "pool_name": { - "title": "Pool Name", - "type": "string" - }, - "scaleset_id": { - "format": "uuid", - "title": "Scaleset Id", - "type": "string" - } - }, - "required": [ - "machine_id", - "pool_name" - ], - "title": "EventNodeHeartbeat", - "type": "object" - }, - "EventNodeStateUpdated": { - "properties": { - "machine_id": { - "format": "uuid", - "title": "Machine Id", - "type": "string" - }, - "pool_name": { - "title": "Pool Name", - "type": "string" - }, - "scaleset_id": { - "format": "uuid", - "title": "Scaleset Id", - "type": "string" - }, - "state": { - "$ref": "#/definitions/NodeState" - } - }, - "required": [ - "machine_id", - "pool_name", - "state" - ], - "title": "EventNodeStateUpdated", - "type": "object" - }, - "EventPing": { - "properties": { - "ping_id": { - "format": "uuid", - "title": "Ping Id", - "type": "string" - } - }, - "required": [ - "ping_id" - ], - "title": "EventPing", - "type": "object" - }, - "EventPoolCreated": { - "properties": { - "arch": { - "$ref": "#/definitions/Architecture" - }, - "autoscale": { - "$ref": "#/definitions/AutoScaleConfig" - }, - "managed": { - "title": "Managed", - "type": "boolean" - }, - "os": { - "$ref": "#/definitions/OS" - }, - "pool_name": { - "title": "Pool Name", - "type": "string" - } - }, - "required": [ - "pool_name", - "os", - "arch", - "managed" - ], - "title": "EventPoolCreated", - "type": "object" - }, - "EventPoolDeleted": { - "properties": { - "pool_name": { - "title": "Pool Name", - "type": "string" - } - }, - "required": [ - "pool_name" - ], - "title": "EventPoolDeleted", - "type": "object" - }, - "EventProxyCreated": { - "properties": { - "proxy_id": { - "format": "uuid", - "title": "Proxy Id", - "type": "string" - }, - "region": { - "title": "Region", - "type": "string" - } - }, - "required": [ - "region", - "proxy_id" - ], - "title": "EventProxyCreated", - "type": "object" - }, - "EventProxyDeleted": { - "properties": { - "proxy_id": { - "format": "uuid", - "title": "Proxy Id", - "type": "string" - }, - "region": { - "title": "Region", - "type": "string" - } - }, - "required": [ - "region", - "proxy_id" - ], - "title": "EventProxyDeleted", - "type": "object" - }, - "EventProxyFailed": { - "properties": { - "error": { - "$ref": "#/definitions/Error" - }, - "proxy_id": { - "format": "uuid", - "title": "Proxy Id", - "type": "string" - }, - "region": { - "title": "Region", - "type": "string" - } - }, - "required": [ - "region", - "proxy_id", - "error" - ], - "title": "EventProxyFailed", - "type": "object" - }, - "EventRegressionReported": { - "properties": { - "container": { - "title": "Container", - "type": "string" - }, - "filename": { - "title": "Filename", - "type": "string" - }, - "regression_report": { - "$ref": "#/definitions/RegressionReport" - }, - "task_config": { - "$ref": "#/definitions/TaskConfig" - } - }, - "required": [ - "regression_report", - "container", - "filename" - ], - "title": "EventRegressionReported", - "type": "object" - }, - "EventScalesetCreated": { - "properties": { - "image": { - "title": "Image", - "type": "string" - }, - "pool_name": { - "title": "Pool Name", - "type": "string" - }, - "region": { - "title": "Region", - "type": "string" - }, - "scaleset_id": { - "format": "uuid", - "title": "Scaleset Id", - "type": "string" - }, - "size": { - "title": "Size", - "type": "integer" - }, - "vm_sku": { - "title": "Vm Sku", - "type": "string" - } - }, - "required": [ - "scaleset_id", - "pool_name", - "vm_sku", - "image", - "region", - "size" - ], - "title": "EventScalesetCreated", - "type": "object" - }, - "EventScalesetDeleted": { - "properties": { - "pool_name": { - "title": "Pool Name", - "type": "string" - }, - "scaleset_id": { - "format": "uuid", - "title": "Scaleset Id", - "type": "string" - } - }, - "required": [ - "scaleset_id", - "pool_name" - ], - "title": "EventScalesetDeleted", - "type": "object" - }, - "EventScalesetFailed": { - "properties": { - "error": { - "$ref": "#/definitions/Error" - }, - "pool_name": { - "title": "Pool Name", - "type": "string" - }, - "scaleset_id": { - "format": "uuid", - "title": "Scaleset Id", - "type": "string" - } - }, - "required": [ - "scaleset_id", - "pool_name", - "error" - ], - "title": "EventScalesetFailed", - "type": "object" - }, - "EventTaskCreated": { - "properties": { - "config": { - "$ref": "#/definitions/TaskConfig" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "task_id": { - "format": "uuid", - "title": "Task Id", - "type": "string" - }, - "user_info": { - "$ref": "#/definitions/UserInfo" - } - }, - "required": [ - "job_id", - "task_id", - "config" - ], - "title": "EventTaskCreated", - "type": "object" - }, - "EventTaskFailed": { - "properties": { - "config": { - "$ref": "#/definitions/TaskConfig" - }, - "error": { - "$ref": "#/definitions/Error" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "task_id": { - "format": "uuid", - "title": "Task Id", - "type": "string" - }, - "user_info": { - "$ref": "#/definitions/UserInfo" - } - }, - "required": [ - "job_id", - "task_id", - "error", - "config" - ], - "title": "EventTaskFailed", - "type": "object" - }, - "EventTaskHeartbeat": { - "properties": { - "config": { - "$ref": "#/definitions/TaskConfig" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "task_id": { - "format": "uuid", - "title": "Task Id", - "type": "string" - } - }, - "required": [ - "job_id", - "task_id", - "config" - ], - "title": "EventTaskHeartbeat", - "type": "object" - }, - "EventTaskStateUpdated": { - "properties": { - "config": { - "$ref": "#/definitions/TaskConfig" - }, - "end_time": { - "format": "date-time", - "title": "End Time", - "type": "string" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "state": { - "$ref": "#/definitions/TaskState" - }, - "task_id": { - "format": "uuid", - "title": "Task Id", - "type": "string" - } - }, - "required": [ - "job_id", - "task_id", - "state", - "config" - ], - "title": "EventTaskStateUpdated", - "type": "object" - }, - "EventTaskStopped": { - "properties": { - "config": { - "$ref": "#/definitions/TaskConfig" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "task_id": { - "format": "uuid", - "title": "Task Id", - "type": "string" - }, - "user_info": { - "$ref": "#/definitions/UserInfo" - } - }, - "required": [ - "job_id", - "task_id", - "config" - ], - "title": "EventTaskStopped", - "type": "object" - }, - "EventType": { - "description": "An enumeration.", - "enum": [ - "job_created", - "job_stopped", - "node_created", - "node_deleted", - "node_state_updated", - "ping", - "pool_created", - "pool_deleted", - "proxy_created", - "proxy_deleted", - "proxy_failed", - "scaleset_created", - "scaleset_deleted", - "scaleset_failed", - "task_created", - "task_failed", - "task_state_updated", - "task_stopped", - "crash_reported", - "regression_reported", - "file_added", - "task_heartbeat", - "node_heartbeat" - ], - "title": "EventType" - }, - "JobConfig": { - "properties": { - "build": { - "title": "Build", - "type": "string" - }, - "duration": { - "title": "Duration", - "type": "integer" - }, - "name": { - "title": "Name", - "type": "string" - }, - "project": { - "title": "Project", - "type": "string" - } - }, - "required": [ - "project", - "name", - "build", - "duration" - ], - "title": "JobConfig", - "type": "object" - }, - "JobTaskStopped": { - "properties": { - "error": { - "$ref": "#/definitions/Error" - }, - "task_id": { - "format": "uuid", - "title": "Task Id", - "type": "string" - }, - "task_type": { - "$ref": "#/definitions/TaskType" - } - }, - "required": [ - "task_id", - "task_type" - ], - "title": "JobTaskStopped", - "type": "object" - }, - "NoReproReport": { - "properties": { - "error": { - "title": "Error", - "type": "string" - }, - "executable": { - "title": "Executable", - "type": "string" - }, - "input_blob": { - "$ref": "#/definitions/BlobRef" - }, - "input_sha256": { - "title": "Input Sha256", - "type": "string" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "task_id": { - "format": "uuid", - "title": "Task Id", - "type": "string" - }, - "tries": { - "title": "Tries", - "type": "integer" - } - }, - "required": [ - "input_sha256", - "executable", - "task_id", - "job_id", - "tries" - ], - "title": "NoReproReport", - "type": "object" - }, - "NodeState": { - "description": "An enumeration.", - "enum": [ - "init", - "free", - "setting_up", - "rebooting", - "ready", - "busy", - "done", - "shutdown", - "halt" - ], - "title": "NodeState" - }, - "OS": { - "description": "An enumeration.", - "enum": [ - "windows", - "linux" - ], - "title": "OS" - }, - "RegressionReport": { - "properties": { - "crash_test_result": { - "$ref": "#/definitions/CrashTestResult" - }, - "original_crash_test_result": { - "$ref": "#/definitions/CrashTestResult" - } - }, - "required": [ - "crash_test_result" - ], - "title": "RegressionReport", - "type": "object" - }, - "Report": { - "properties": { - "asan_log": { - "title": "Asan Log", - "type": "string" - }, - "call_stack": { - "items": { - "type": "string" - }, - "title": "Call Stack", - "type": "array" - }, - "call_stack_sha256": { - "title": "Call Stack Sha256", - "type": "string" - }, - "crash_site": { - "title": "Crash Site", - "type": "string" - }, - "crash_type": { - "title": "Crash Type", - "type": "string" - }, - "executable": { - "title": "Executable", - "type": "string" - }, - "input_blob": { - "$ref": "#/definitions/BlobRef" - }, - "input_sha256": { - "title": "Input Sha256", - "type": "string" - }, - "input_url": { - "title": "Input Url", - "type": "string" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "minimized_stack": { - "items": { - "type": "string" - }, - "title": "Minimized Stack", - "type": "array" - }, - "minimized_stack_function_names": { - "items": { - "type": "string" - }, - "title": "Minimized Stack Function Names", - "type": "array" - }, - "minimized_stack_function_names_sha256": { - "title": "Minimized Stack Function Names Sha256", - "type": "string" - }, - "minimized_stack_sha256": { - "title": "Minimized Stack Sha256", - "type": "string" - }, - "scariness_description": { - "title": "Scariness Description", - "type": "string" - }, - "scariness_score": { - "title": "Scariness Score", - "type": "integer" - }, - "task_id": { - "format": "uuid", - "title": "Task Id", - "type": "string" - } - }, - "required": [ - "executable", - "crash_type", - "crash_site", - "call_stack", - "call_stack_sha256", - "input_sha256", - "task_id", - "job_id" - ], - "title": "Report", - "type": "object" - }, - "StatsFormat": { - "description": "An enumeration.", - "enum": [ - "AFL" - ], - "title": "StatsFormat" - }, - "TaskConfig": { - "properties": { - "colocate": { - "title": "Colocate", - "type": "boolean" - }, - "containers": { - "items": { - "$ref": "#/definitions/TaskContainers" - }, - "title": "Containers", - "type": "array" - }, - "debug": { - "items": { - "$ref": "#/definitions/TaskDebugFlag" - }, - "type": "array" - }, - "job_id": { - "format": "uuid", - "title": "Job Id", - "type": "string" - }, - "pool": { - "$ref": "#/definitions/TaskPool" - }, - "prereq_tasks": { - "items": { - "format": "uuid", - "type": "string" - }, - "title": "Prereq Tasks", - "type": "array" - }, - "tags": { - "additionalProperties": { - "type": "string" - }, - "title": "Tags", - "type": "object" - }, - "task": { - "$ref": "#/definitions/TaskDetails" - }, - "vm": { - "$ref": "#/definitions/TaskVm" - } - }, - "required": [ - "job_id", - "task", - "containers", - "tags" - ], - "title": "TaskConfig", - "type": "object" - }, - "TaskContainers": { - "properties": { - "name": { - "title": "Name", - "type": "string" - }, - "type": { - "$ref": "#/definitions/ContainerType" - } - }, - "required": [ - "type", - "name" - ], - "title": "TaskContainers", - "type": "object" - }, - "TaskDebugFlag": { - "description": "An enumeration.", - "enum": [ - "keep_node_on_failure", - "keep_node_on_completion" - ], - "title": "TaskDebugFlag" - }, - "TaskDetails": { - "properties": { - "analyzer_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Analyzer Env", - "type": "object" - }, - "analyzer_exe": { - "title": "Analyzer Exe", - "type": "string" - }, - "analyzer_options": { - "items": { - "type": "string" - }, - "title": "Analyzer Options", - "type": "array" - }, - "check_asan_log": { - "title": "Check Asan Log", - "type": "boolean" - }, - "check_debugger": { - "default": true, - "title": "Check Debugger", - "type": "boolean" - }, - "check_fuzzer_help": { - "title": "Check Fuzzer Help", - "type": "boolean" - }, - "check_retry_count": { - "title": "Check Retry Count", - "type": "integer" - }, - "duration": { - "title": "Duration", - "type": "integer" - }, - "ensemble_sync_delay": { - "title": "Ensemble Sync Delay", - "type": "integer" - }, - "expect_crash_on_failure": { - "title": "Expect Crash On Failure", - "type": "boolean" - }, - "generator_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Generator Env", - "type": "object" - }, - "generator_exe": { - "title": "Generator Exe", - "type": "string" - }, - "generator_options": { - "items": { - "type": "string" - }, - "title": "Generator Options", - "type": "array" - }, - "minimized_stack_depth": { - "title": "Minimized Stack Depth", - "type": "integer" - }, - "preserve_existing_outputs": { - "title": "Preserve Existing Outputs", - "type": "boolean" - }, - "reboot_after_setup": { - "title": "Reboot After Setup", - "type": "boolean" - }, - "rename_output": { - "title": "Rename Output", - "type": "boolean" - }, - "report_list": { - "items": { - "type": "string" - }, - "title": "Report List", - "type": "array" - }, - "stats_file": { - "title": "Stats File", - "type": "string" - }, - "stats_format": { - "$ref": "#/definitions/StatsFormat" - }, - "supervisor_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Supervisor Env", - "type": "object" - }, - "supervisor_exe": { - "title": "Supervisor Exe", - "type": "string" - }, - "supervisor_input_marker": { - "title": "Supervisor Input Marker", - "type": "string" - }, - "supervisor_options": { - "items": { - "type": "string" - }, - "title": "Supervisor Options", - "type": "array" - }, - "target_env": { - "additionalProperties": { - "type": "string" - }, - "title": "Target Env", - "type": "object" - }, - "target_exe": { - "title": "Target Exe", - "type": "string" - }, - "target_options": { - "items": { - "type": "string" - }, - "title": "Target Options", - "type": "array" - }, - "target_options_merge": { - "title": "Target Options Merge", - "type": "boolean" - }, - "target_timeout": { - "title": "Target Timeout", - "type": "integer" - }, - "target_workers": { - "title": "Target Workers", - "type": "integer" - }, - "type": { - "$ref": "#/definitions/TaskType" - }, - "wait_for_files": { - "$ref": "#/definitions/ContainerType" - } - }, - "required": [ - "type", - "duration" - ], - "title": "TaskDetails", - "type": "object" - }, - "TaskPool": { - "properties": { - "count": { - "title": "Count", - "type": "integer" - }, - "pool_name": { - "title": "Pool Name", - "type": "string" - } - }, - "required": [ - "count", - "pool_name" - ], - "title": "TaskPool", - "type": "object" - }, - "TaskState": { - "description": "An enumeration.", - "enum": [ - "init", - "waiting", - "scheduled", - "setting_up", - "running", - "stopping", - "stopped", - "wait_job" - ], - "title": "TaskState" - }, - "TaskType": { - "description": "An enumeration.", - "enum": [ - "libfuzzer_fuzz", - "libfuzzer_coverage", - "libfuzzer_crash_report", - "libfuzzer_merge", - "libfuzzer_regression", - "generic_analysis", - "generic_supervisor", - "generic_merge", - "generic_generator", - "generic_crash_report", - "generic_regression" - ], - "title": "TaskType" - }, - "TaskVm": { - "properties": { - "count": { - "default": 1, - "title": "Count", - "type": "integer" - }, - "image": { - "title": "Image", - "type": "string" - }, - "reboot_after_setup": { - "title": "Reboot After Setup", - "type": "boolean" - }, - "region": { - "title": "Region", - "type": "string" - }, - "sku": { - "title": "Sku", - "type": "string" - }, - "spot_instances": { - "default": false, - "title": "Spot Instances", - "type": "boolean" - } - }, - "required": [ - "region", - "sku", - "image" - ], - "title": "TaskVm", - "type": "object" - }, - "UserInfo": { - "properties": { - "application_id": { - "format": "uuid", - "title": "Application Id", - "type": "string" - }, - "object_id": { - "format": "uuid", - "title": "Object Id", - "type": "string" - }, - "upn": { - "title": "Upn", - "type": "string" - } - }, - "title": "UserInfo", - "type": "object" - } - }, - "properties": { - "event": { - "anyOf": [ - { - "$ref": "#/definitions/EventJobCreated" - }, - { - "$ref": "#/definitions/EventJobStopped" - }, - { - "$ref": "#/definitions/EventNodeStateUpdated" - }, - { - "$ref": "#/definitions/EventNodeCreated" - }, - { - "$ref": "#/definitions/EventNodeDeleted" - }, - { - "$ref": "#/definitions/EventNodeHeartbeat" - }, - { - "$ref": "#/definitions/EventPing" - }, - { - "$ref": "#/definitions/EventPoolCreated" - }, - { - "$ref": "#/definitions/EventPoolDeleted" - }, - { - "$ref": "#/definitions/EventProxyFailed" - }, - { - "$ref": "#/definitions/EventProxyCreated" - }, - { - "$ref": "#/definitions/EventProxyDeleted" - }, - { - "$ref": "#/definitions/EventScalesetFailed" - }, - { - "$ref": "#/definitions/EventScalesetCreated" - }, - { - "$ref": "#/definitions/EventScalesetDeleted" - }, - { - "$ref": "#/definitions/EventTaskFailed" - }, - { - "$ref": "#/definitions/EventTaskStateUpdated" - }, - { - "$ref": "#/definitions/EventTaskCreated" - }, - { - "$ref": "#/definitions/EventTaskStopped" - }, - { - "$ref": "#/definitions/EventTaskHeartbeat" - }, - { - "$ref": "#/definitions/EventCrashReported" - }, - { - "$ref": "#/definitions/EventRegressionReported" - }, - { - "$ref": "#/definitions/EventFileAdded" - } - ], - "title": "Event" - }, - "event_id": { - "format": "uuid", - "title": "Event Id", - "type": "string" - }, - "event_type": { - "$ref": "#/definitions/EventType" - }, - "instance_id": { - "format": "uuid", - "title": "Instance Id", - "type": "string" - }, - "instance_name": { - "title": "Instance Name", - "type": "string" - }, - "webhook_id": { - "format": "uuid", - "title": "Webhook Id", - "type": "string" - } - }, - "required": [ - "event_type", - "event", - "instance_id", - "instance_name", - "webhook_id" - ], - "title": "WebhookMessage", - "type": "object" -} -``` - From b1551783c8a1bc89446c56935cbdc6fd432c1642 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 11 May 2021 16:34:06 -0700 Subject: [PATCH 052/110] Fixing webhood_events.md and generate-docs. --- docs/webhook_events.md | 5861 ++++++++++++++++++++++++++++ src/pytypes/extra/generate-docs.py | 50 +- 2 files changed, 5884 insertions(+), 27 deletions(-) diff --git a/docs/webhook_events.md b/docs/webhook_events.md index cb6fae74b7..dec9a506cc 100644 --- a/docs/webhook_events.md +++ b/docs/webhook_events.md @@ -1,3 +1,28 @@ +# Webhook Events + +This document describes the basic webhook event subscriptions available in OneFuzz + +## Payload + +Each event will be submitted via HTTP POST to the user provided URL. + +### Example + +```json +{ + "event": { + "ping_id": "00000000-0000-0000-0000-000000000000" + }, + "event_id": "00000000-0000-0000-0000-000000000000", + "event_type": "ping", + "instance_id": "00000000-0000-0000-0000-000000000000", + "instance_name": "example", + "webhook_id": "00000000-0000-0000-0000-000000000000" +} +``` + +## Event Types (EventType) + * [crash_reported](#crash_reported) * [file_added](#file_added) * [job_created](#job_created) @@ -22,3 +47,5839 @@ * [task_state_updated](#task_state_updated) * [task_stopped](#task_stopped) +### crash_reported + +#### Example + +```json +{ + "container": "container-name", + "filename": "example.json", + "report": { + "asan_log": "example asan log", + "call_stack": [ + "#0 line", + "#1 line", + "#2 line" + ], + "call_stack_sha256": "0000000000000000000000000000000000000000000000000000000000000000", + "crash_site": "example crash site", + "crash_type": "example crash report type", + "executable": "fuzz.exe", + "input_blob": { + "account": "contoso-storage-account", + "container": "crashes", + "name": "input.txt" + }, + "input_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "job_id": "00000000-0000-0000-0000-000000000000", + "scariness_description": "example-scariness", + "scariness_score": 10, + "task_id": "00000000-0000-0000-0000-000000000000" + } +} +``` + +#### Schema + +```json +{ + "definitions": { + "BlobRef": { + "properties": { + "account": { + "title": "Account", + "type": "string" + }, + "container": { + "title": "Container", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + } + }, + "required": [ + "account", + "container", + "name" + ], + "title": "BlobRef", + "type": "object" + }, + "ContainerType": { + "description": "An enumeration.", + "enum": [ + "analysis", + "coverage", + "crashes", + "inputs", + "no_repro", + "readonly_inputs", + "reports", + "setup", + "tools", + "unique_inputs", + "unique_reports", + "regression_reports" + ], + "title": "ContainerType" + }, + "Report": { + "properties": { + "asan_log": { + "title": "Asan Log", + "type": "string" + }, + "call_stack": { + "items": { + "type": "string" + }, + "title": "Call Stack", + "type": "array" + }, + "call_stack_sha256": { + "title": "Call Stack Sha256", + "type": "string" + }, + "crash_site": { + "title": "Crash Site", + "type": "string" + }, + "crash_type": { + "title": "Crash Type", + "type": "string" + }, + "executable": { + "title": "Executable", + "type": "string" + }, + "input_blob": { + "$ref": "#/definitions/BlobRef" + }, + "input_sha256": { + "title": "Input Sha256", + "type": "string" + }, + "input_url": { + "title": "Input Url", + "type": "string" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "minimized_stack": { + "items": { + "type": "string" + }, + "title": "Minimized Stack", + "type": "array" + }, + "minimized_stack_function_names": { + "items": { + "type": "string" + }, + "title": "Minimized Stack Function Names", + "type": "array" + }, + "minimized_stack_function_names_sha256": { + "title": "Minimized Stack Function Names Sha256", + "type": "string" + }, + "minimized_stack_sha256": { + "title": "Minimized Stack Sha256", + "type": "string" + }, + "scariness_description": { + "title": "Scariness Description", + "type": "string" + }, + "scariness_score": { + "title": "Scariness Score", + "type": "integer" + }, + "task_id": { + "format": "uuid", + "title": "Task Id", + "type": "string" + } + }, + "required": [ + "executable", + "crash_type", + "crash_site", + "call_stack", + "call_stack_sha256", + "input_sha256", + "task_id", + "job_id" + ], + "title": "Report", + "type": "object" + }, + "StatsFormat": { + "description": "An enumeration.", + "enum": [ + "AFL" + ], + "title": "StatsFormat" + }, + "TaskConfig": { + "properties": { + "colocate": { + "title": "Colocate", + "type": "boolean" + }, + "containers": { + "items": { + "$ref": "#/definitions/TaskContainers" + }, + "title": "Containers", + "type": "array" + }, + "debug": { + "items": { + "$ref": "#/definitions/TaskDebugFlag" + }, + "type": "array" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "pool": { + "$ref": "#/definitions/TaskPool" + }, + "prereq_tasks": { + "items": { + "format": "uuid", + "type": "string" + }, + "title": "Prereq Tasks", + "type": "array" + }, + "tags": { + "additionalProperties": { + "type": "string" + }, + "title": "Tags", + "type": "object" + }, + "task": { + "$ref": "#/definitions/TaskDetails" + }, + "vm": { + "$ref": "#/definitions/TaskVm" + } + }, + "required": [ + "job_id", + "task", + "containers", + "tags" + ], + "title": "TaskConfig", + "type": "object" + }, + "TaskContainers": { + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "type": { + "$ref": "#/definitions/ContainerType" + } + }, + "required": [ + "type", + "name" + ], + "title": "TaskContainers", + "type": "object" + }, + "TaskDebugFlag": { + "description": "An enumeration.", + "enum": [ + "keep_node_on_failure", + "keep_node_on_completion" + ], + "title": "TaskDebugFlag" + }, + "TaskDetails": { + "properties": { + "analyzer_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Analyzer Env", + "type": "object" + }, + "analyzer_exe": { + "title": "Analyzer Exe", + "type": "string" + }, + "analyzer_options": { + "items": { + "type": "string" + }, + "title": "Analyzer Options", + "type": "array" + }, + "check_asan_log": { + "title": "Check Asan Log", + "type": "boolean" + }, + "check_debugger": { + "default": true, + "title": "Check Debugger", + "type": "boolean" + }, + "check_fuzzer_help": { + "title": "Check Fuzzer Help", + "type": "boolean" + }, + "check_retry_count": { + "title": "Check Retry Count", + "type": "integer" + }, + "duration": { + "title": "Duration", + "type": "integer" + }, + "ensemble_sync_delay": { + "title": "Ensemble Sync Delay", + "type": "integer" + }, + "expect_crash_on_failure": { + "title": "Expect Crash On Failure", + "type": "boolean" + }, + "generator_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Generator Env", + "type": "object" + }, + "generator_exe": { + "title": "Generator Exe", + "type": "string" + }, + "generator_options": { + "items": { + "type": "string" + }, + "title": "Generator Options", + "type": "array" + }, + "minimized_stack_depth": { + "title": "Minimized Stack Depth", + "type": "integer" + }, + "preserve_existing_outputs": { + "title": "Preserve Existing Outputs", + "type": "boolean" + }, + "reboot_after_setup": { + "title": "Reboot After Setup", + "type": "boolean" + }, + "rename_output": { + "title": "Rename Output", + "type": "boolean" + }, + "report_list": { + "items": { + "type": "string" + }, + "title": "Report List", + "type": "array" + }, + "stats_file": { + "title": "Stats File", + "type": "string" + }, + "stats_format": { + "$ref": "#/definitions/StatsFormat" + }, + "supervisor_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Supervisor Env", + "type": "object" + }, + "supervisor_exe": { + "title": "Supervisor Exe", + "type": "string" + }, + "supervisor_input_marker": { + "title": "Supervisor Input Marker", + "type": "string" + }, + "supervisor_options": { + "items": { + "type": "string" + }, + "title": "Supervisor Options", + "type": "array" + }, + "target_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Target Env", + "type": "object" + }, + "target_exe": { + "title": "Target Exe", + "type": "string" + }, + "target_options": { + "items": { + "type": "string" + }, + "title": "Target Options", + "type": "array" + }, + "target_options_merge": { + "title": "Target Options Merge", + "type": "boolean" + }, + "target_timeout": { + "title": "Target Timeout", + "type": "integer" + }, + "target_workers": { + "title": "Target Workers", + "type": "integer" + }, + "type": { + "$ref": "#/definitions/TaskType" + }, + "wait_for_files": { + "$ref": "#/definitions/ContainerType" + } + }, + "required": [ + "type", + "duration" + ], + "title": "TaskDetails", + "type": "object" + }, + "TaskPool": { + "properties": { + "count": { + "title": "Count", + "type": "integer" + }, + "pool_name": { + "title": "Pool Name", + "type": "string" + } + }, + "required": [ + "count", + "pool_name" + ], + "title": "TaskPool", + "type": "object" + }, + "TaskType": { + "description": "An enumeration.", + "enum": [ + "libfuzzer_fuzz", + "libfuzzer_coverage", + "libfuzzer_crash_report", + "libfuzzer_merge", + "libfuzzer_regression", + "generic_analysis", + "generic_supervisor", + "generic_merge", + "generic_generator", + "generic_crash_report", + "generic_regression" + ], + "title": "TaskType" + }, + "TaskVm": { + "properties": { + "count": { + "default": 1, + "title": "Count", + "type": "integer" + }, + "image": { + "title": "Image", + "type": "string" + }, + "reboot_after_setup": { + "title": "Reboot After Setup", + "type": "boolean" + }, + "region": { + "title": "Region", + "type": "string" + }, + "sku": { + "title": "Sku", + "type": "string" + }, + "spot_instances": { + "default": false, + "title": "Spot Instances", + "type": "boolean" + } + }, + "required": [ + "region", + "sku", + "image" + ], + "title": "TaskVm", + "type": "object" + } + }, + "properties": { + "container": { + "title": "Container", + "type": "string" + }, + "filename": { + "title": "Filename", + "type": "string" + }, + "report": { + "$ref": "#/definitions/Report" + }, + "task_config": { + "$ref": "#/definitions/TaskConfig" + } + }, + "required": [ + "report", + "container", + "filename" + ], + "title": "EventCrashReported", + "type": "object" +} +``` + +### file_added + +#### Example + +```json +{ + "container": "container-name", + "filename": "example.txt" +} +``` + +#### Schema + +```json +{ + "properties": { + "container": { + "title": "Container", + "type": "string" + }, + "filename": { + "title": "Filename", + "type": "string" + } + }, + "required": [ + "container", + "filename" + ], + "title": "EventFileAdded", + "type": "object" +} +``` + +### job_created + +#### Example + +```json +{ + "config": { + "build": "build 1", + "duration": 24, + "name": "example name", + "project": "example project" + }, + "job_id": "00000000-0000-0000-0000-000000000000" +} +``` + +#### Schema + +```json +{ + "definitions": { + "JobConfig": { + "properties": { + "build": { + "title": "Build", + "type": "string" + }, + "duration": { + "title": "Duration", + "type": "integer" + }, + "name": { + "title": "Name", + "type": "string" + }, + "project": { + "title": "Project", + "type": "string" + } + }, + "required": [ + "project", + "name", + "build", + "duration" + ], + "title": "JobConfig", + "type": "object" + }, + "UserInfo": { + "properties": { + "application_id": { + "format": "uuid", + "title": "Application Id", + "type": "string" + }, + "object_id": { + "format": "uuid", + "title": "Object Id", + "type": "string" + }, + "upn": { + "title": "Upn", + "type": "string" + } + }, + "title": "UserInfo", + "type": "object" + } + }, + "properties": { + "config": { + "$ref": "#/definitions/JobConfig" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "user_info": { + "$ref": "#/definitions/UserInfo" + } + }, + "required": [ + "job_id", + "config" + ], + "title": "EventJobCreated", + "type": "object" +} +``` + +### job_stopped + +#### Example + +```json +{ + "config": { + "build": "build 1", + "duration": 24, + "name": "example name", + "project": "example project" + }, + "job_id": "00000000-0000-0000-0000-000000000000", + "task_info": [ + { + "error": { + "code": 468, + "errors": [ + "example error message" + ] + }, + "task_id": "00000000-0000-0000-0000-000000000000", + "task_type": "libfuzzer_fuzz" + }, + { + "task_id": "00000000-0000-0000-0000-000000000001", + "task_type": "libfuzzer_coverage" + } + ] +} +``` + +#### Schema + +```json +{ + "definitions": { + "Error": { + "properties": { + "code": { + "$ref": "#/definitions/ErrorCode" + }, + "errors": { + "items": { + "type": "string" + }, + "title": "Errors", + "type": "array" + } + }, + "required": [ + "code", + "errors" + ], + "title": "Error", + "type": "object" + }, + "ErrorCode": { + "description": "An enumeration.", + "enum": [ + 450, + 451, + 452, + 453, + 454, + 455, + 456, + 457, + 458, + 459, + 460, + 461, + 462, + 463, + 464, + 465, + 467, + 468, + 469, + 470, + 471, + 472 + ], + "title": "ErrorCode" + }, + "JobConfig": { + "properties": { + "build": { + "title": "Build", + "type": "string" + }, + "duration": { + "title": "Duration", + "type": "integer" + }, + "name": { + "title": "Name", + "type": "string" + }, + "project": { + "title": "Project", + "type": "string" + } + }, + "required": [ + "project", + "name", + "build", + "duration" + ], + "title": "JobConfig", + "type": "object" + }, + "JobTaskStopped": { + "properties": { + "error": { + "$ref": "#/definitions/Error" + }, + "task_id": { + "format": "uuid", + "title": "Task Id", + "type": "string" + }, + "task_type": { + "$ref": "#/definitions/TaskType" + } + }, + "required": [ + "task_id", + "task_type" + ], + "title": "JobTaskStopped", + "type": "object" + }, + "TaskType": { + "description": "An enumeration.", + "enum": [ + "libfuzzer_fuzz", + "libfuzzer_coverage", + "libfuzzer_crash_report", + "libfuzzer_merge", + "libfuzzer_regression", + "generic_analysis", + "generic_supervisor", + "generic_merge", + "generic_generator", + "generic_crash_report", + "generic_regression" + ], + "title": "TaskType" + }, + "UserInfo": { + "properties": { + "application_id": { + "format": "uuid", + "title": "Application Id", + "type": "string" + }, + "object_id": { + "format": "uuid", + "title": "Object Id", + "type": "string" + }, + "upn": { + "title": "Upn", + "type": "string" + } + }, + "title": "UserInfo", + "type": "object" + } + }, + "properties": { + "config": { + "$ref": "#/definitions/JobConfig" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "task_info": { + "items": { + "$ref": "#/definitions/JobTaskStopped" + }, + "title": "Task Info", + "type": "array" + }, + "user_info": { + "$ref": "#/definitions/UserInfo" + } + }, + "required": [ + "job_id", + "config" + ], + "title": "EventJobStopped", + "type": "object" +} +``` + +### node_created + +#### Example + +```json +{ + "machine_id": "00000000-0000-0000-0000-000000000000", + "pool_name": "example" +} +``` + +#### Schema + +```json +{ + "properties": { + "machine_id": { + "format": "uuid", + "title": "Machine Id", + "type": "string" + }, + "pool_name": { + "title": "Pool Name", + "type": "string" + }, + "scaleset_id": { + "format": "uuid", + "title": "Scaleset Id", + "type": "string" + } + }, + "required": [ + "machine_id", + "pool_name" + ], + "title": "EventNodeCreated", + "type": "object" +} +``` + +### node_deleted + +#### Example + +```json +{ + "machine_id": "00000000-0000-0000-0000-000000000000", + "pool_name": "example" +} +``` + +#### Schema + +```json +{ + "properties": { + "machine_id": { + "format": "uuid", + "title": "Machine Id", + "type": "string" + }, + "pool_name": { + "title": "Pool Name", + "type": "string" + }, + "scaleset_id": { + "format": "uuid", + "title": "Scaleset Id", + "type": "string" + } + }, + "required": [ + "machine_id", + "pool_name" + ], + "title": "EventNodeDeleted", + "type": "object" +} +``` + +### node_heartbeat + +#### Example + +```json +{ + "machine_id": "00000000-0000-0000-0000-000000000000", + "pool_name": "example" +} +``` + +#### Schema + +```json +{ + "properties": { + "machine_id": { + "format": "uuid", + "title": "Machine Id", + "type": "string" + }, + "pool_name": { + "title": "Pool Name", + "type": "string" + }, + "scaleset_id": { + "format": "uuid", + "title": "Scaleset Id", + "type": "string" + } + }, + "required": [ + "machine_id", + "pool_name" + ], + "title": "EventNodeHeartbeat", + "type": "object" +} +``` + +### node_state_updated + +#### Example + +```json +{ + "machine_id": "00000000-0000-0000-0000-000000000000", + "pool_name": "example", + "state": "setting_up" +} +``` + +#### Schema + +```json +{ + "definitions": { + "NodeState": { + "description": "An enumeration.", + "enum": [ + "init", + "free", + "setting_up", + "rebooting", + "ready", + "busy", + "done", + "shutdown", + "halt" + ], + "title": "NodeState" + } + }, + "properties": { + "machine_id": { + "format": "uuid", + "title": "Machine Id", + "type": "string" + }, + "pool_name": { + "title": "Pool Name", + "type": "string" + }, + "scaleset_id": { + "format": "uuid", + "title": "Scaleset Id", + "type": "string" + }, + "state": { + "$ref": "#/definitions/NodeState" + } + }, + "required": [ + "machine_id", + "pool_name", + "state" + ], + "title": "EventNodeStateUpdated", + "type": "object" +} +``` + +### ping + +#### Example + +```json +{ + "ping_id": "00000000-0000-0000-0000-000000000000" +} +``` + +#### Schema + +```json +{ + "properties": { + "ping_id": { + "format": "uuid", + "title": "Ping Id", + "type": "string" + } + }, + "required": [ + "ping_id" + ], + "title": "EventPing", + "type": "object" +} +``` + +### pool_created + +#### Example + +```json +{ + "arch": "x86_64", + "managed": true, + "os": "linux", + "pool_name": "example" +} +``` + +#### Schema + +```json +{ + "definitions": { + "Architecture": { + "description": "An enumeration.", + "enum": [ + "x86_64" + ], + "title": "Architecture" + }, + "AutoScaleConfig": { + "properties": { + "ephemeral_os_disks": { + "default": false, + "title": "Ephemeral Os Disks", + "type": "boolean" + }, + "image": { + "title": "Image", + "type": "string" + }, + "max_size": { + "title": "Max Size", + "type": "integer" + }, + "min_size": { + "default": 0, + "title": "Min Size", + "type": "integer" + }, + "region": { + "title": "Region", + "type": "string" + }, + "scaleset_size": { + "title": "Scaleset Size", + "type": "integer" + }, + "spot_instances": { + "default": false, + "title": "Spot Instances", + "type": "boolean" + }, + "vm_sku": { + "title": "Vm Sku", + "type": "string" + } + }, + "required": [ + "image", + "scaleset_size", + "vm_sku" + ], + "title": "AutoScaleConfig", + "type": "object" + }, + "OS": { + "description": "An enumeration.", + "enum": [ + "windows", + "linux" + ], + "title": "OS" + } + }, + "properties": { + "arch": { + "$ref": "#/definitions/Architecture" + }, + "autoscale": { + "$ref": "#/definitions/AutoScaleConfig" + }, + "managed": { + "title": "Managed", + "type": "boolean" + }, + "os": { + "$ref": "#/definitions/OS" + }, + "pool_name": { + "title": "Pool Name", + "type": "string" + } + }, + "required": [ + "pool_name", + "os", + "arch", + "managed" + ], + "title": "EventPoolCreated", + "type": "object" +} +``` + +### pool_deleted + +#### Example + +```json +{ + "pool_name": "example" +} +``` + +#### Schema + +```json +{ + "properties": { + "pool_name": { + "title": "Pool Name", + "type": "string" + } + }, + "required": [ + "pool_name" + ], + "title": "EventPoolDeleted", + "type": "object" +} +``` + +### proxy_created + +#### Example + +```json +{ + "proxy_id": "00000000-0000-0000-0000-000000000000", + "region": "eastus" +} +``` + +#### Schema + +```json +{ + "properties": { + "proxy_id": { + "format": "uuid", + "title": "Proxy Id", + "type": "string" + }, + "region": { + "title": "Region", + "type": "string" + } + }, + "required": [ + "region", + "proxy_id" + ], + "title": "EventProxyCreated", + "type": "object" +} +``` + +### proxy_deleted + +#### Example + +```json +{ + "proxy_id": "00000000-0000-0000-0000-000000000000", + "region": "eastus" +} +``` + +#### Schema + +```json +{ + "properties": { + "proxy_id": { + "format": "uuid", + "title": "Proxy Id", + "type": "string" + }, + "region": { + "title": "Region", + "type": "string" + } + }, + "required": [ + "region", + "proxy_id" + ], + "title": "EventProxyDeleted", + "type": "object" +} +``` + +### proxy_failed + +#### Example + +```json +{ + "error": { + "code": 472, + "errors": [ + "example error message" + ] + }, + "proxy_id": "00000000-0000-0000-0000-000000000000", + "region": "eastus" +} +``` + +#### Schema + +```json +{ + "definitions": { + "Error": { + "properties": { + "code": { + "$ref": "#/definitions/ErrorCode" + }, + "errors": { + "items": { + "type": "string" + }, + "title": "Errors", + "type": "array" + } + }, + "required": [ + "code", + "errors" + ], + "title": "Error", + "type": "object" + }, + "ErrorCode": { + "description": "An enumeration.", + "enum": [ + 450, + 451, + 452, + 453, + 454, + 455, + 456, + 457, + 458, + 459, + 460, + 461, + 462, + 463, + 464, + 465, + 467, + 468, + 469, + 470, + 471, + 472 + ], + "title": "ErrorCode" + } + }, + "properties": { + "error": { + "$ref": "#/definitions/Error" + }, + "proxy_id": { + "format": "uuid", + "title": "Proxy Id", + "type": "string" + }, + "region": { + "title": "Region", + "type": "string" + } + }, + "required": [ + "region", + "proxy_id", + "error" + ], + "title": "EventProxyFailed", + "type": "object" +} +``` + +### regression_reported + +#### Example + +```json +{ + "container": "container-name", + "filename": "example.json", + "regression_report": { + "crash_test_result": { + "crash_report": { + "asan_log": "example asan log", + "call_stack": [ + "#0 line", + "#1 line", + "#2 line" + ], + "call_stack_sha256": "0000000000000000000000000000000000000000000000000000000000000000", + "crash_site": "example crash site", + "crash_type": "example crash report type", + "executable": "fuzz.exe", + "input_blob": { + "account": "contoso-storage-account", + "container": "crashes", + "name": "input.txt" + }, + "input_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "job_id": "00000000-0000-0000-0000-000000000000", + "scariness_description": "example-scariness", + "scariness_score": 10, + "task_id": "00000000-0000-0000-0000-000000000000" + } + }, + "original_crash_test_result": { + "crash_report": { + "asan_log": "example asan log", + "call_stack": [ + "#0 line", + "#1 line", + "#2 line" + ], + "call_stack_sha256": "0000000000000000000000000000000000000000000000000000000000000000", + "crash_site": "example crash site", + "crash_type": "example crash report type", + "executable": "fuzz.exe", + "input_blob": { + "account": "contoso-storage-account", + "container": "crashes", + "name": "input.txt" + }, + "input_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "job_id": "00000000-0000-0000-0000-000000000000", + "scariness_description": "example-scariness", + "scariness_score": 10, + "task_id": "00000000-0000-0000-0000-000000000000" + } + } + } +} +``` + +#### Schema + +```json +{ + "definitions": { + "BlobRef": { + "properties": { + "account": { + "title": "Account", + "type": "string" + }, + "container": { + "title": "Container", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + } + }, + "required": [ + "account", + "container", + "name" + ], + "title": "BlobRef", + "type": "object" + }, + "ContainerType": { + "description": "An enumeration.", + "enum": [ + "analysis", + "coverage", + "crashes", + "inputs", + "no_repro", + "readonly_inputs", + "reports", + "setup", + "tools", + "unique_inputs", + "unique_reports", + "regression_reports" + ], + "title": "ContainerType" + }, + "CrashTestResult": { + "properties": { + "crash_report": { + "$ref": "#/definitions/Report" + }, + "no_repro": { + "$ref": "#/definitions/NoReproReport" + } + }, + "title": "CrashTestResult", + "type": "object" + }, + "NoReproReport": { + "properties": { + "error": { + "title": "Error", + "type": "string" + }, + "executable": { + "title": "Executable", + "type": "string" + }, + "input_blob": { + "$ref": "#/definitions/BlobRef" + }, + "input_sha256": { + "title": "Input Sha256", + "type": "string" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "task_id": { + "format": "uuid", + "title": "Task Id", + "type": "string" + }, + "tries": { + "title": "Tries", + "type": "integer" + } + }, + "required": [ + "input_sha256", + "executable", + "task_id", + "job_id", + "tries" + ], + "title": "NoReproReport", + "type": "object" + }, + "RegressionReport": { + "properties": { + "crash_test_result": { + "$ref": "#/definitions/CrashTestResult" + }, + "original_crash_test_result": { + "$ref": "#/definitions/CrashTestResult" + } + }, + "required": [ + "crash_test_result" + ], + "title": "RegressionReport", + "type": "object" + }, + "Report": { + "properties": { + "asan_log": { + "title": "Asan Log", + "type": "string" + }, + "call_stack": { + "items": { + "type": "string" + }, + "title": "Call Stack", + "type": "array" + }, + "call_stack_sha256": { + "title": "Call Stack Sha256", + "type": "string" + }, + "crash_site": { + "title": "Crash Site", + "type": "string" + }, + "crash_type": { + "title": "Crash Type", + "type": "string" + }, + "executable": { + "title": "Executable", + "type": "string" + }, + "input_blob": { + "$ref": "#/definitions/BlobRef" + }, + "input_sha256": { + "title": "Input Sha256", + "type": "string" + }, + "input_url": { + "title": "Input Url", + "type": "string" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "minimized_stack": { + "items": { + "type": "string" + }, + "title": "Minimized Stack", + "type": "array" + }, + "minimized_stack_function_names": { + "items": { + "type": "string" + }, + "title": "Minimized Stack Function Names", + "type": "array" + }, + "minimized_stack_function_names_sha256": { + "title": "Minimized Stack Function Names Sha256", + "type": "string" + }, + "minimized_stack_sha256": { + "title": "Minimized Stack Sha256", + "type": "string" + }, + "scariness_description": { + "title": "Scariness Description", + "type": "string" + }, + "scariness_score": { + "title": "Scariness Score", + "type": "integer" + }, + "task_id": { + "format": "uuid", + "title": "Task Id", + "type": "string" + } + }, + "required": [ + "executable", + "crash_type", + "crash_site", + "call_stack", + "call_stack_sha256", + "input_sha256", + "task_id", + "job_id" + ], + "title": "Report", + "type": "object" + }, + "StatsFormat": { + "description": "An enumeration.", + "enum": [ + "AFL" + ], + "title": "StatsFormat" + }, + "TaskConfig": { + "properties": { + "colocate": { + "title": "Colocate", + "type": "boolean" + }, + "containers": { + "items": { + "$ref": "#/definitions/TaskContainers" + }, + "title": "Containers", + "type": "array" + }, + "debug": { + "items": { + "$ref": "#/definitions/TaskDebugFlag" + }, + "type": "array" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "pool": { + "$ref": "#/definitions/TaskPool" + }, + "prereq_tasks": { + "items": { + "format": "uuid", + "type": "string" + }, + "title": "Prereq Tasks", + "type": "array" + }, + "tags": { + "additionalProperties": { + "type": "string" + }, + "title": "Tags", + "type": "object" + }, + "task": { + "$ref": "#/definitions/TaskDetails" + }, + "vm": { + "$ref": "#/definitions/TaskVm" + } + }, + "required": [ + "job_id", + "task", + "containers", + "tags" + ], + "title": "TaskConfig", + "type": "object" + }, + "TaskContainers": { + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "type": { + "$ref": "#/definitions/ContainerType" + } + }, + "required": [ + "type", + "name" + ], + "title": "TaskContainers", + "type": "object" + }, + "TaskDebugFlag": { + "description": "An enumeration.", + "enum": [ + "keep_node_on_failure", + "keep_node_on_completion" + ], + "title": "TaskDebugFlag" + }, + "TaskDetails": { + "properties": { + "analyzer_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Analyzer Env", + "type": "object" + }, + "analyzer_exe": { + "title": "Analyzer Exe", + "type": "string" + }, + "analyzer_options": { + "items": { + "type": "string" + }, + "title": "Analyzer Options", + "type": "array" + }, + "check_asan_log": { + "title": "Check Asan Log", + "type": "boolean" + }, + "check_debugger": { + "default": true, + "title": "Check Debugger", + "type": "boolean" + }, + "check_fuzzer_help": { + "title": "Check Fuzzer Help", + "type": "boolean" + }, + "check_retry_count": { + "title": "Check Retry Count", + "type": "integer" + }, + "duration": { + "title": "Duration", + "type": "integer" + }, + "ensemble_sync_delay": { + "title": "Ensemble Sync Delay", + "type": "integer" + }, + "expect_crash_on_failure": { + "title": "Expect Crash On Failure", + "type": "boolean" + }, + "generator_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Generator Env", + "type": "object" + }, + "generator_exe": { + "title": "Generator Exe", + "type": "string" + }, + "generator_options": { + "items": { + "type": "string" + }, + "title": "Generator Options", + "type": "array" + }, + "minimized_stack_depth": { + "title": "Minimized Stack Depth", + "type": "integer" + }, + "preserve_existing_outputs": { + "title": "Preserve Existing Outputs", + "type": "boolean" + }, + "reboot_after_setup": { + "title": "Reboot After Setup", + "type": "boolean" + }, + "rename_output": { + "title": "Rename Output", + "type": "boolean" + }, + "report_list": { + "items": { + "type": "string" + }, + "title": "Report List", + "type": "array" + }, + "stats_file": { + "title": "Stats File", + "type": "string" + }, + "stats_format": { + "$ref": "#/definitions/StatsFormat" + }, + "supervisor_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Supervisor Env", + "type": "object" + }, + "supervisor_exe": { + "title": "Supervisor Exe", + "type": "string" + }, + "supervisor_input_marker": { + "title": "Supervisor Input Marker", + "type": "string" + }, + "supervisor_options": { + "items": { + "type": "string" + }, + "title": "Supervisor Options", + "type": "array" + }, + "target_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Target Env", + "type": "object" + }, + "target_exe": { + "title": "Target Exe", + "type": "string" + }, + "target_options": { + "items": { + "type": "string" + }, + "title": "Target Options", + "type": "array" + }, + "target_options_merge": { + "title": "Target Options Merge", + "type": "boolean" + }, + "target_timeout": { + "title": "Target Timeout", + "type": "integer" + }, + "target_workers": { + "title": "Target Workers", + "type": "integer" + }, + "type": { + "$ref": "#/definitions/TaskType" + }, + "wait_for_files": { + "$ref": "#/definitions/ContainerType" + } + }, + "required": [ + "type", + "duration" + ], + "title": "TaskDetails", + "type": "object" + }, + "TaskPool": { + "properties": { + "count": { + "title": "Count", + "type": "integer" + }, + "pool_name": { + "title": "Pool Name", + "type": "string" + } + }, + "required": [ + "count", + "pool_name" + ], + "title": "TaskPool", + "type": "object" + }, + "TaskType": { + "description": "An enumeration.", + "enum": [ + "libfuzzer_fuzz", + "libfuzzer_coverage", + "libfuzzer_crash_report", + "libfuzzer_merge", + "libfuzzer_regression", + "generic_analysis", + "generic_supervisor", + "generic_merge", + "generic_generator", + "generic_crash_report", + "generic_regression" + ], + "title": "TaskType" + }, + "TaskVm": { + "properties": { + "count": { + "default": 1, + "title": "Count", + "type": "integer" + }, + "image": { + "title": "Image", + "type": "string" + }, + "reboot_after_setup": { + "title": "Reboot After Setup", + "type": "boolean" + }, + "region": { + "title": "Region", + "type": "string" + }, + "sku": { + "title": "Sku", + "type": "string" + }, + "spot_instances": { + "default": false, + "title": "Spot Instances", + "type": "boolean" + } + }, + "required": [ + "region", + "sku", + "image" + ], + "title": "TaskVm", + "type": "object" + } + }, + "properties": { + "container": { + "title": "Container", + "type": "string" + }, + "filename": { + "title": "Filename", + "type": "string" + }, + "regression_report": { + "$ref": "#/definitions/RegressionReport" + }, + "task_config": { + "$ref": "#/definitions/TaskConfig" + } + }, + "required": [ + "regression_report", + "container", + "filename" + ], + "title": "EventRegressionReported", + "type": "object" +} +``` + +### scaleset_created + +#### Example + +```json +{ + "image": "Canonical:UbuntuServer:18.04-LTS:latest", + "pool_name": "example", + "region": "eastus", + "scaleset_id": "00000000-0000-0000-0000-000000000000", + "size": 10, + "vm_sku": "Standard_D2s_v3" +} +``` + +#### Schema + +```json +{ + "properties": { + "image": { + "title": "Image", + "type": "string" + }, + "pool_name": { + "title": "Pool Name", + "type": "string" + }, + "region": { + "title": "Region", + "type": "string" + }, + "scaleset_id": { + "format": "uuid", + "title": "Scaleset Id", + "type": "string" + }, + "size": { + "title": "Size", + "type": "integer" + }, + "vm_sku": { + "title": "Vm Sku", + "type": "string" + } + }, + "required": [ + "scaleset_id", + "pool_name", + "vm_sku", + "image", + "region", + "size" + ], + "title": "EventScalesetCreated", + "type": "object" +} +``` + +### scaleset_deleted + +#### Example + +```json +{ + "pool_name": "example", + "scaleset_id": "00000000-0000-0000-0000-000000000000" +} +``` + +#### Schema + +```json +{ + "properties": { + "pool_name": { + "title": "Pool Name", + "type": "string" + }, + "scaleset_id": { + "format": "uuid", + "title": "Scaleset Id", + "type": "string" + } + }, + "required": [ + "scaleset_id", + "pool_name" + ], + "title": "EventScalesetDeleted", + "type": "object" +} +``` + +### scaleset_failed + +#### Example + +```json +{ + "error": { + "code": 456, + "errors": [ + "example error message" + ] + }, + "pool_name": "example", + "scaleset_id": "00000000-0000-0000-0000-000000000000" +} +``` + +#### Schema + +```json +{ + "definitions": { + "Error": { + "properties": { + "code": { + "$ref": "#/definitions/ErrorCode" + }, + "errors": { + "items": { + "type": "string" + }, + "title": "Errors", + "type": "array" + } + }, + "required": [ + "code", + "errors" + ], + "title": "Error", + "type": "object" + }, + "ErrorCode": { + "description": "An enumeration.", + "enum": [ + 450, + 451, + 452, + 453, + 454, + 455, + 456, + 457, + 458, + 459, + 460, + 461, + 462, + 463, + 464, + 465, + 467, + 468, + 469, + 470, + 471, + 472 + ], + "title": "ErrorCode" + } + }, + "properties": { + "error": { + "$ref": "#/definitions/Error" + }, + "pool_name": { + "title": "Pool Name", + "type": "string" + }, + "scaleset_id": { + "format": "uuid", + "title": "Scaleset Id", + "type": "string" + } + }, + "required": [ + "scaleset_id", + "pool_name", + "error" + ], + "title": "EventScalesetFailed", + "type": "object" +} +``` + +### task_created + +#### Example + +```json +{ + "config": { + "containers": [ + { + "name": "my-setup", + "type": "setup" + }, + { + "name": "my-inputs", + "type": "inputs" + }, + { + "name": "my-crashes", + "type": "crashes" + } + ], + "job_id": "00000000-0000-0000-0000-000000000000", + "tags": {}, + "task": { + "check_debugger": true, + "duration": 1, + "target_env": {}, + "target_exe": "fuzz.exe", + "target_options": [], + "type": "libfuzzer_fuzz" + } + }, + "job_id": "00000000-0000-0000-0000-000000000000", + "task_id": "00000000-0000-0000-0000-000000000000", + "user_info": { + "application_id": "00000000-0000-0000-0000-000000000000", + "object_id": "00000000-0000-0000-0000-000000000000", + "upn": "example@contoso.com" + } +} +``` + +#### Schema + +```json +{ + "definitions": { + "ContainerType": { + "description": "An enumeration.", + "enum": [ + "analysis", + "coverage", + "crashes", + "inputs", + "no_repro", + "readonly_inputs", + "reports", + "setup", + "tools", + "unique_inputs", + "unique_reports", + "regression_reports" + ], + "title": "ContainerType" + }, + "StatsFormat": { + "description": "An enumeration.", + "enum": [ + "AFL" + ], + "title": "StatsFormat" + }, + "TaskConfig": { + "properties": { + "colocate": { + "title": "Colocate", + "type": "boolean" + }, + "containers": { + "items": { + "$ref": "#/definitions/TaskContainers" + }, + "title": "Containers", + "type": "array" + }, + "debug": { + "items": { + "$ref": "#/definitions/TaskDebugFlag" + }, + "type": "array" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "pool": { + "$ref": "#/definitions/TaskPool" + }, + "prereq_tasks": { + "items": { + "format": "uuid", + "type": "string" + }, + "title": "Prereq Tasks", + "type": "array" + }, + "tags": { + "additionalProperties": { + "type": "string" + }, + "title": "Tags", + "type": "object" + }, + "task": { + "$ref": "#/definitions/TaskDetails" + }, + "vm": { + "$ref": "#/definitions/TaskVm" + } + }, + "required": [ + "job_id", + "task", + "containers", + "tags" + ], + "title": "TaskConfig", + "type": "object" + }, + "TaskContainers": { + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "type": { + "$ref": "#/definitions/ContainerType" + } + }, + "required": [ + "type", + "name" + ], + "title": "TaskContainers", + "type": "object" + }, + "TaskDebugFlag": { + "description": "An enumeration.", + "enum": [ + "keep_node_on_failure", + "keep_node_on_completion" + ], + "title": "TaskDebugFlag" + }, + "TaskDetails": { + "properties": { + "analyzer_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Analyzer Env", + "type": "object" + }, + "analyzer_exe": { + "title": "Analyzer Exe", + "type": "string" + }, + "analyzer_options": { + "items": { + "type": "string" + }, + "title": "Analyzer Options", + "type": "array" + }, + "check_asan_log": { + "title": "Check Asan Log", + "type": "boolean" + }, + "check_debugger": { + "default": true, + "title": "Check Debugger", + "type": "boolean" + }, + "check_fuzzer_help": { + "title": "Check Fuzzer Help", + "type": "boolean" + }, + "check_retry_count": { + "title": "Check Retry Count", + "type": "integer" + }, + "duration": { + "title": "Duration", + "type": "integer" + }, + "ensemble_sync_delay": { + "title": "Ensemble Sync Delay", + "type": "integer" + }, + "expect_crash_on_failure": { + "title": "Expect Crash On Failure", + "type": "boolean" + }, + "generator_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Generator Env", + "type": "object" + }, + "generator_exe": { + "title": "Generator Exe", + "type": "string" + }, + "generator_options": { + "items": { + "type": "string" + }, + "title": "Generator Options", + "type": "array" + }, + "minimized_stack_depth": { + "title": "Minimized Stack Depth", + "type": "integer" + }, + "preserve_existing_outputs": { + "title": "Preserve Existing Outputs", + "type": "boolean" + }, + "reboot_after_setup": { + "title": "Reboot After Setup", + "type": "boolean" + }, + "rename_output": { + "title": "Rename Output", + "type": "boolean" + }, + "report_list": { + "items": { + "type": "string" + }, + "title": "Report List", + "type": "array" + }, + "stats_file": { + "title": "Stats File", + "type": "string" + }, + "stats_format": { + "$ref": "#/definitions/StatsFormat" + }, + "supervisor_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Supervisor Env", + "type": "object" + }, + "supervisor_exe": { + "title": "Supervisor Exe", + "type": "string" + }, + "supervisor_input_marker": { + "title": "Supervisor Input Marker", + "type": "string" + }, + "supervisor_options": { + "items": { + "type": "string" + }, + "title": "Supervisor Options", + "type": "array" + }, + "target_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Target Env", + "type": "object" + }, + "target_exe": { + "title": "Target Exe", + "type": "string" + }, + "target_options": { + "items": { + "type": "string" + }, + "title": "Target Options", + "type": "array" + }, + "target_options_merge": { + "title": "Target Options Merge", + "type": "boolean" + }, + "target_timeout": { + "title": "Target Timeout", + "type": "integer" + }, + "target_workers": { + "title": "Target Workers", + "type": "integer" + }, + "type": { + "$ref": "#/definitions/TaskType" + }, + "wait_for_files": { + "$ref": "#/definitions/ContainerType" + } + }, + "required": [ + "type", + "duration" + ], + "title": "TaskDetails", + "type": "object" + }, + "TaskPool": { + "properties": { + "count": { + "title": "Count", + "type": "integer" + }, + "pool_name": { + "title": "Pool Name", + "type": "string" + } + }, + "required": [ + "count", + "pool_name" + ], + "title": "TaskPool", + "type": "object" + }, + "TaskType": { + "description": "An enumeration.", + "enum": [ + "libfuzzer_fuzz", + "libfuzzer_coverage", + "libfuzzer_crash_report", + "libfuzzer_merge", + "libfuzzer_regression", + "generic_analysis", + "generic_supervisor", + "generic_merge", + "generic_generator", + "generic_crash_report", + "generic_regression" + ], + "title": "TaskType" + }, + "TaskVm": { + "properties": { + "count": { + "default": 1, + "title": "Count", + "type": "integer" + }, + "image": { + "title": "Image", + "type": "string" + }, + "reboot_after_setup": { + "title": "Reboot After Setup", + "type": "boolean" + }, + "region": { + "title": "Region", + "type": "string" + }, + "sku": { + "title": "Sku", + "type": "string" + }, + "spot_instances": { + "default": false, + "title": "Spot Instances", + "type": "boolean" + } + }, + "required": [ + "region", + "sku", + "image" + ], + "title": "TaskVm", + "type": "object" + }, + "UserInfo": { + "properties": { + "application_id": { + "format": "uuid", + "title": "Application Id", + "type": "string" + }, + "object_id": { + "format": "uuid", + "title": "Object Id", + "type": "string" + }, + "upn": { + "title": "Upn", + "type": "string" + } + }, + "title": "UserInfo", + "type": "object" + } + }, + "properties": { + "config": { + "$ref": "#/definitions/TaskConfig" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "task_id": { + "format": "uuid", + "title": "Task Id", + "type": "string" + }, + "user_info": { + "$ref": "#/definitions/UserInfo" + } + }, + "required": [ + "job_id", + "task_id", + "config" + ], + "title": "EventTaskCreated", + "type": "object" +} +``` + +### task_failed + +#### Example + +```json +{ + "config": { + "containers": [ + { + "name": "my-setup", + "type": "setup" + }, + { + "name": "my-inputs", + "type": "inputs" + }, + { + "name": "my-crashes", + "type": "crashes" + } + ], + "job_id": "00000000-0000-0000-0000-000000000000", + "tags": {}, + "task": { + "check_debugger": true, + "duration": 1, + "target_env": {}, + "target_exe": "fuzz.exe", + "target_options": [], + "type": "libfuzzer_fuzz" + } + }, + "error": { + "code": 468, + "errors": [ + "example error message" + ] + }, + "job_id": "00000000-0000-0000-0000-000000000000", + "task_id": "00000000-0000-0000-0000-000000000000", + "user_info": { + "application_id": "00000000-0000-0000-0000-000000000000", + "object_id": "00000000-0000-0000-0000-000000000000", + "upn": "example@contoso.com" + } +} +``` + +#### Schema + +```json +{ + "definitions": { + "ContainerType": { + "description": "An enumeration.", + "enum": [ + "analysis", + "coverage", + "crashes", + "inputs", + "no_repro", + "readonly_inputs", + "reports", + "setup", + "tools", + "unique_inputs", + "unique_reports", + "regression_reports" + ], + "title": "ContainerType" + }, + "Error": { + "properties": { + "code": { + "$ref": "#/definitions/ErrorCode" + }, + "errors": { + "items": { + "type": "string" + }, + "title": "Errors", + "type": "array" + } + }, + "required": [ + "code", + "errors" + ], + "title": "Error", + "type": "object" + }, + "ErrorCode": { + "description": "An enumeration.", + "enum": [ + 450, + 451, + 452, + 453, + 454, + 455, + 456, + 457, + 458, + 459, + 460, + 461, + 462, + 463, + 464, + 465, + 467, + 468, + 469, + 470, + 471, + 472 + ], + "title": "ErrorCode" + }, + "StatsFormat": { + "description": "An enumeration.", + "enum": [ + "AFL" + ], + "title": "StatsFormat" + }, + "TaskConfig": { + "properties": { + "colocate": { + "title": "Colocate", + "type": "boolean" + }, + "containers": { + "items": { + "$ref": "#/definitions/TaskContainers" + }, + "title": "Containers", + "type": "array" + }, + "debug": { + "items": { + "$ref": "#/definitions/TaskDebugFlag" + }, + "type": "array" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "pool": { + "$ref": "#/definitions/TaskPool" + }, + "prereq_tasks": { + "items": { + "format": "uuid", + "type": "string" + }, + "title": "Prereq Tasks", + "type": "array" + }, + "tags": { + "additionalProperties": { + "type": "string" + }, + "title": "Tags", + "type": "object" + }, + "task": { + "$ref": "#/definitions/TaskDetails" + }, + "vm": { + "$ref": "#/definitions/TaskVm" + } + }, + "required": [ + "job_id", + "task", + "containers", + "tags" + ], + "title": "TaskConfig", + "type": "object" + }, + "TaskContainers": { + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "type": { + "$ref": "#/definitions/ContainerType" + } + }, + "required": [ + "type", + "name" + ], + "title": "TaskContainers", + "type": "object" + }, + "TaskDebugFlag": { + "description": "An enumeration.", + "enum": [ + "keep_node_on_failure", + "keep_node_on_completion" + ], + "title": "TaskDebugFlag" + }, + "TaskDetails": { + "properties": { + "analyzer_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Analyzer Env", + "type": "object" + }, + "analyzer_exe": { + "title": "Analyzer Exe", + "type": "string" + }, + "analyzer_options": { + "items": { + "type": "string" + }, + "title": "Analyzer Options", + "type": "array" + }, + "check_asan_log": { + "title": "Check Asan Log", + "type": "boolean" + }, + "check_debugger": { + "default": true, + "title": "Check Debugger", + "type": "boolean" + }, + "check_fuzzer_help": { + "title": "Check Fuzzer Help", + "type": "boolean" + }, + "check_retry_count": { + "title": "Check Retry Count", + "type": "integer" + }, + "duration": { + "title": "Duration", + "type": "integer" + }, + "ensemble_sync_delay": { + "title": "Ensemble Sync Delay", + "type": "integer" + }, + "expect_crash_on_failure": { + "title": "Expect Crash On Failure", + "type": "boolean" + }, + "generator_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Generator Env", + "type": "object" + }, + "generator_exe": { + "title": "Generator Exe", + "type": "string" + }, + "generator_options": { + "items": { + "type": "string" + }, + "title": "Generator Options", + "type": "array" + }, + "minimized_stack_depth": { + "title": "Minimized Stack Depth", + "type": "integer" + }, + "preserve_existing_outputs": { + "title": "Preserve Existing Outputs", + "type": "boolean" + }, + "reboot_after_setup": { + "title": "Reboot After Setup", + "type": "boolean" + }, + "rename_output": { + "title": "Rename Output", + "type": "boolean" + }, + "report_list": { + "items": { + "type": "string" + }, + "title": "Report List", + "type": "array" + }, + "stats_file": { + "title": "Stats File", + "type": "string" + }, + "stats_format": { + "$ref": "#/definitions/StatsFormat" + }, + "supervisor_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Supervisor Env", + "type": "object" + }, + "supervisor_exe": { + "title": "Supervisor Exe", + "type": "string" + }, + "supervisor_input_marker": { + "title": "Supervisor Input Marker", + "type": "string" + }, + "supervisor_options": { + "items": { + "type": "string" + }, + "title": "Supervisor Options", + "type": "array" + }, + "target_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Target Env", + "type": "object" + }, + "target_exe": { + "title": "Target Exe", + "type": "string" + }, + "target_options": { + "items": { + "type": "string" + }, + "title": "Target Options", + "type": "array" + }, + "target_options_merge": { + "title": "Target Options Merge", + "type": "boolean" + }, + "target_timeout": { + "title": "Target Timeout", + "type": "integer" + }, + "target_workers": { + "title": "Target Workers", + "type": "integer" + }, + "type": { + "$ref": "#/definitions/TaskType" + }, + "wait_for_files": { + "$ref": "#/definitions/ContainerType" + } + }, + "required": [ + "type", + "duration" + ], + "title": "TaskDetails", + "type": "object" + }, + "TaskPool": { + "properties": { + "count": { + "title": "Count", + "type": "integer" + }, + "pool_name": { + "title": "Pool Name", + "type": "string" + } + }, + "required": [ + "count", + "pool_name" + ], + "title": "TaskPool", + "type": "object" + }, + "TaskType": { + "description": "An enumeration.", + "enum": [ + "libfuzzer_fuzz", + "libfuzzer_coverage", + "libfuzzer_crash_report", + "libfuzzer_merge", + "libfuzzer_regression", + "generic_analysis", + "generic_supervisor", + "generic_merge", + "generic_generator", + "generic_crash_report", + "generic_regression" + ], + "title": "TaskType" + }, + "TaskVm": { + "properties": { + "count": { + "default": 1, + "title": "Count", + "type": "integer" + }, + "image": { + "title": "Image", + "type": "string" + }, + "reboot_after_setup": { + "title": "Reboot After Setup", + "type": "boolean" + }, + "region": { + "title": "Region", + "type": "string" + }, + "sku": { + "title": "Sku", + "type": "string" + }, + "spot_instances": { + "default": false, + "title": "Spot Instances", + "type": "boolean" + } + }, + "required": [ + "region", + "sku", + "image" + ], + "title": "TaskVm", + "type": "object" + }, + "UserInfo": { + "properties": { + "application_id": { + "format": "uuid", + "title": "Application Id", + "type": "string" + }, + "object_id": { + "format": "uuid", + "title": "Object Id", + "type": "string" + }, + "upn": { + "title": "Upn", + "type": "string" + } + }, + "title": "UserInfo", + "type": "object" + } + }, + "properties": { + "config": { + "$ref": "#/definitions/TaskConfig" + }, + "error": { + "$ref": "#/definitions/Error" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "task_id": { + "format": "uuid", + "title": "Task Id", + "type": "string" + }, + "user_info": { + "$ref": "#/definitions/UserInfo" + } + }, + "required": [ + "job_id", + "task_id", + "error", + "config" + ], + "title": "EventTaskFailed", + "type": "object" +} +``` + +### task_heartbeat + +#### Example + +```json +{ + "config": { + "containers": [ + { + "name": "my-setup", + "type": "setup" + }, + { + "name": "my-inputs", + "type": "inputs" + }, + { + "name": "my-crashes", + "type": "crashes" + } + ], + "job_id": "00000000-0000-0000-0000-000000000000", + "tags": {}, + "task": { + "check_debugger": true, + "duration": 1, + "target_env": {}, + "target_exe": "fuzz.exe", + "target_options": [], + "type": "libfuzzer_fuzz" + } + }, + "job_id": "00000000-0000-0000-0000-000000000000", + "task_id": "00000000-0000-0000-0000-000000000000" +} +``` + +#### Schema + +```json +{ + "definitions": { + "ContainerType": { + "description": "An enumeration.", + "enum": [ + "analysis", + "coverage", + "crashes", + "inputs", + "no_repro", + "readonly_inputs", + "reports", + "setup", + "tools", + "unique_inputs", + "unique_reports", + "regression_reports" + ], + "title": "ContainerType" + }, + "StatsFormat": { + "description": "An enumeration.", + "enum": [ + "AFL" + ], + "title": "StatsFormat" + }, + "TaskConfig": { + "properties": { + "colocate": { + "title": "Colocate", + "type": "boolean" + }, + "containers": { + "items": { + "$ref": "#/definitions/TaskContainers" + }, + "title": "Containers", + "type": "array" + }, + "debug": { + "items": { + "$ref": "#/definitions/TaskDebugFlag" + }, + "type": "array" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "pool": { + "$ref": "#/definitions/TaskPool" + }, + "prereq_tasks": { + "items": { + "format": "uuid", + "type": "string" + }, + "title": "Prereq Tasks", + "type": "array" + }, + "tags": { + "additionalProperties": { + "type": "string" + }, + "title": "Tags", + "type": "object" + }, + "task": { + "$ref": "#/definitions/TaskDetails" + }, + "vm": { + "$ref": "#/definitions/TaskVm" + } + }, + "required": [ + "job_id", + "task", + "containers", + "tags" + ], + "title": "TaskConfig", + "type": "object" + }, + "TaskContainers": { + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "type": { + "$ref": "#/definitions/ContainerType" + } + }, + "required": [ + "type", + "name" + ], + "title": "TaskContainers", + "type": "object" + }, + "TaskDebugFlag": { + "description": "An enumeration.", + "enum": [ + "keep_node_on_failure", + "keep_node_on_completion" + ], + "title": "TaskDebugFlag" + }, + "TaskDetails": { + "properties": { + "analyzer_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Analyzer Env", + "type": "object" + }, + "analyzer_exe": { + "title": "Analyzer Exe", + "type": "string" + }, + "analyzer_options": { + "items": { + "type": "string" + }, + "title": "Analyzer Options", + "type": "array" + }, + "check_asan_log": { + "title": "Check Asan Log", + "type": "boolean" + }, + "check_debugger": { + "default": true, + "title": "Check Debugger", + "type": "boolean" + }, + "check_fuzzer_help": { + "title": "Check Fuzzer Help", + "type": "boolean" + }, + "check_retry_count": { + "title": "Check Retry Count", + "type": "integer" + }, + "duration": { + "title": "Duration", + "type": "integer" + }, + "ensemble_sync_delay": { + "title": "Ensemble Sync Delay", + "type": "integer" + }, + "expect_crash_on_failure": { + "title": "Expect Crash On Failure", + "type": "boolean" + }, + "generator_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Generator Env", + "type": "object" + }, + "generator_exe": { + "title": "Generator Exe", + "type": "string" + }, + "generator_options": { + "items": { + "type": "string" + }, + "title": "Generator Options", + "type": "array" + }, + "minimized_stack_depth": { + "title": "Minimized Stack Depth", + "type": "integer" + }, + "preserve_existing_outputs": { + "title": "Preserve Existing Outputs", + "type": "boolean" + }, + "reboot_after_setup": { + "title": "Reboot After Setup", + "type": "boolean" + }, + "rename_output": { + "title": "Rename Output", + "type": "boolean" + }, + "report_list": { + "items": { + "type": "string" + }, + "title": "Report List", + "type": "array" + }, + "stats_file": { + "title": "Stats File", + "type": "string" + }, + "stats_format": { + "$ref": "#/definitions/StatsFormat" + }, + "supervisor_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Supervisor Env", + "type": "object" + }, + "supervisor_exe": { + "title": "Supervisor Exe", + "type": "string" + }, + "supervisor_input_marker": { + "title": "Supervisor Input Marker", + "type": "string" + }, + "supervisor_options": { + "items": { + "type": "string" + }, + "title": "Supervisor Options", + "type": "array" + }, + "target_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Target Env", + "type": "object" + }, + "target_exe": { + "title": "Target Exe", + "type": "string" + }, + "target_options": { + "items": { + "type": "string" + }, + "title": "Target Options", + "type": "array" + }, + "target_options_merge": { + "title": "Target Options Merge", + "type": "boolean" + }, + "target_timeout": { + "title": "Target Timeout", + "type": "integer" + }, + "target_workers": { + "title": "Target Workers", + "type": "integer" + }, + "type": { + "$ref": "#/definitions/TaskType" + }, + "wait_for_files": { + "$ref": "#/definitions/ContainerType" + } + }, + "required": [ + "type", + "duration" + ], + "title": "TaskDetails", + "type": "object" + }, + "TaskPool": { + "properties": { + "count": { + "title": "Count", + "type": "integer" + }, + "pool_name": { + "title": "Pool Name", + "type": "string" + } + }, + "required": [ + "count", + "pool_name" + ], + "title": "TaskPool", + "type": "object" + }, + "TaskType": { + "description": "An enumeration.", + "enum": [ + "libfuzzer_fuzz", + "libfuzzer_coverage", + "libfuzzer_crash_report", + "libfuzzer_merge", + "libfuzzer_regression", + "generic_analysis", + "generic_supervisor", + "generic_merge", + "generic_generator", + "generic_crash_report", + "generic_regression" + ], + "title": "TaskType" + }, + "TaskVm": { + "properties": { + "count": { + "default": 1, + "title": "Count", + "type": "integer" + }, + "image": { + "title": "Image", + "type": "string" + }, + "reboot_after_setup": { + "title": "Reboot After Setup", + "type": "boolean" + }, + "region": { + "title": "Region", + "type": "string" + }, + "sku": { + "title": "Sku", + "type": "string" + }, + "spot_instances": { + "default": false, + "title": "Spot Instances", + "type": "boolean" + } + }, + "required": [ + "region", + "sku", + "image" + ], + "title": "TaskVm", + "type": "object" + } + }, + "properties": { + "config": { + "$ref": "#/definitions/TaskConfig" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "task_id": { + "format": "uuid", + "title": "Task Id", + "type": "string" + } + }, + "required": [ + "job_id", + "task_id", + "config" + ], + "title": "EventTaskHeartbeat", + "type": "object" +} +``` + +### task_state_updated + +#### Example + +```json +{ + "config": { + "containers": [ + { + "name": "my-setup", + "type": "setup" + }, + { + "name": "my-inputs", + "type": "inputs" + }, + { + "name": "my-crashes", + "type": "crashes" + } + ], + "job_id": "00000000-0000-0000-0000-000000000000", + "tags": {}, + "task": { + "check_debugger": true, + "duration": 1, + "target_env": {}, + "target_exe": "fuzz.exe", + "target_options": [], + "type": "libfuzzer_fuzz" + } + }, + "job_id": "00000000-0000-0000-0000-000000000000", + "state": "init", + "task_id": "00000000-0000-0000-0000-000000000000" +} +``` + +#### Schema + +```json +{ + "definitions": { + "ContainerType": { + "description": "An enumeration.", + "enum": [ + "analysis", + "coverage", + "crashes", + "inputs", + "no_repro", + "readonly_inputs", + "reports", + "setup", + "tools", + "unique_inputs", + "unique_reports", + "regression_reports" + ], + "title": "ContainerType" + }, + "StatsFormat": { + "description": "An enumeration.", + "enum": [ + "AFL" + ], + "title": "StatsFormat" + }, + "TaskConfig": { + "properties": { + "colocate": { + "title": "Colocate", + "type": "boolean" + }, + "containers": { + "items": { + "$ref": "#/definitions/TaskContainers" + }, + "title": "Containers", + "type": "array" + }, + "debug": { + "items": { + "$ref": "#/definitions/TaskDebugFlag" + }, + "type": "array" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "pool": { + "$ref": "#/definitions/TaskPool" + }, + "prereq_tasks": { + "items": { + "format": "uuid", + "type": "string" + }, + "title": "Prereq Tasks", + "type": "array" + }, + "tags": { + "additionalProperties": { + "type": "string" + }, + "title": "Tags", + "type": "object" + }, + "task": { + "$ref": "#/definitions/TaskDetails" + }, + "vm": { + "$ref": "#/definitions/TaskVm" + } + }, + "required": [ + "job_id", + "task", + "containers", + "tags" + ], + "title": "TaskConfig", + "type": "object" + }, + "TaskContainers": { + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "type": { + "$ref": "#/definitions/ContainerType" + } + }, + "required": [ + "type", + "name" + ], + "title": "TaskContainers", + "type": "object" + }, + "TaskDebugFlag": { + "description": "An enumeration.", + "enum": [ + "keep_node_on_failure", + "keep_node_on_completion" + ], + "title": "TaskDebugFlag" + }, + "TaskDetails": { + "properties": { + "analyzer_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Analyzer Env", + "type": "object" + }, + "analyzer_exe": { + "title": "Analyzer Exe", + "type": "string" + }, + "analyzer_options": { + "items": { + "type": "string" + }, + "title": "Analyzer Options", + "type": "array" + }, + "check_asan_log": { + "title": "Check Asan Log", + "type": "boolean" + }, + "check_debugger": { + "default": true, + "title": "Check Debugger", + "type": "boolean" + }, + "check_fuzzer_help": { + "title": "Check Fuzzer Help", + "type": "boolean" + }, + "check_retry_count": { + "title": "Check Retry Count", + "type": "integer" + }, + "duration": { + "title": "Duration", + "type": "integer" + }, + "ensemble_sync_delay": { + "title": "Ensemble Sync Delay", + "type": "integer" + }, + "expect_crash_on_failure": { + "title": "Expect Crash On Failure", + "type": "boolean" + }, + "generator_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Generator Env", + "type": "object" + }, + "generator_exe": { + "title": "Generator Exe", + "type": "string" + }, + "generator_options": { + "items": { + "type": "string" + }, + "title": "Generator Options", + "type": "array" + }, + "minimized_stack_depth": { + "title": "Minimized Stack Depth", + "type": "integer" + }, + "preserve_existing_outputs": { + "title": "Preserve Existing Outputs", + "type": "boolean" + }, + "reboot_after_setup": { + "title": "Reboot After Setup", + "type": "boolean" + }, + "rename_output": { + "title": "Rename Output", + "type": "boolean" + }, + "report_list": { + "items": { + "type": "string" + }, + "title": "Report List", + "type": "array" + }, + "stats_file": { + "title": "Stats File", + "type": "string" + }, + "stats_format": { + "$ref": "#/definitions/StatsFormat" + }, + "supervisor_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Supervisor Env", + "type": "object" + }, + "supervisor_exe": { + "title": "Supervisor Exe", + "type": "string" + }, + "supervisor_input_marker": { + "title": "Supervisor Input Marker", + "type": "string" + }, + "supervisor_options": { + "items": { + "type": "string" + }, + "title": "Supervisor Options", + "type": "array" + }, + "target_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Target Env", + "type": "object" + }, + "target_exe": { + "title": "Target Exe", + "type": "string" + }, + "target_options": { + "items": { + "type": "string" + }, + "title": "Target Options", + "type": "array" + }, + "target_options_merge": { + "title": "Target Options Merge", + "type": "boolean" + }, + "target_timeout": { + "title": "Target Timeout", + "type": "integer" + }, + "target_workers": { + "title": "Target Workers", + "type": "integer" + }, + "type": { + "$ref": "#/definitions/TaskType" + }, + "wait_for_files": { + "$ref": "#/definitions/ContainerType" + } + }, + "required": [ + "type", + "duration" + ], + "title": "TaskDetails", + "type": "object" + }, + "TaskPool": { + "properties": { + "count": { + "title": "Count", + "type": "integer" + }, + "pool_name": { + "title": "Pool Name", + "type": "string" + } + }, + "required": [ + "count", + "pool_name" + ], + "title": "TaskPool", + "type": "object" + }, + "TaskState": { + "description": "An enumeration.", + "enum": [ + "init", + "waiting", + "scheduled", + "setting_up", + "running", + "stopping", + "stopped", + "wait_job" + ], + "title": "TaskState" + }, + "TaskType": { + "description": "An enumeration.", + "enum": [ + "libfuzzer_fuzz", + "libfuzzer_coverage", + "libfuzzer_crash_report", + "libfuzzer_merge", + "libfuzzer_regression", + "generic_analysis", + "generic_supervisor", + "generic_merge", + "generic_generator", + "generic_crash_report", + "generic_regression" + ], + "title": "TaskType" + }, + "TaskVm": { + "properties": { + "count": { + "default": 1, + "title": "Count", + "type": "integer" + }, + "image": { + "title": "Image", + "type": "string" + }, + "reboot_after_setup": { + "title": "Reboot After Setup", + "type": "boolean" + }, + "region": { + "title": "Region", + "type": "string" + }, + "sku": { + "title": "Sku", + "type": "string" + }, + "spot_instances": { + "default": false, + "title": "Spot Instances", + "type": "boolean" + } + }, + "required": [ + "region", + "sku", + "image" + ], + "title": "TaskVm", + "type": "object" + } + }, + "properties": { + "config": { + "$ref": "#/definitions/TaskConfig" + }, + "end_time": { + "format": "date-time", + "title": "End Time", + "type": "string" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "state": { + "$ref": "#/definitions/TaskState" + }, + "task_id": { + "format": "uuid", + "title": "Task Id", + "type": "string" + } + }, + "required": [ + "job_id", + "task_id", + "state", + "config" + ], + "title": "EventTaskStateUpdated", + "type": "object" +} +``` + +### task_stopped + +#### Example + +```json +{ + "config": { + "containers": [ + { + "name": "my-setup", + "type": "setup" + }, + { + "name": "my-inputs", + "type": "inputs" + }, + { + "name": "my-crashes", + "type": "crashes" + } + ], + "job_id": "00000000-0000-0000-0000-000000000000", + "tags": {}, + "task": { + "check_debugger": true, + "duration": 1, + "target_env": {}, + "target_exe": "fuzz.exe", + "target_options": [], + "type": "libfuzzer_fuzz" + } + }, + "job_id": "00000000-0000-0000-0000-000000000000", + "task_id": "00000000-0000-0000-0000-000000000000", + "user_info": { + "application_id": "00000000-0000-0000-0000-000000000000", + "object_id": "00000000-0000-0000-0000-000000000000", + "upn": "example@contoso.com" + } +} +``` + +#### Schema + +```json +{ + "definitions": { + "ContainerType": { + "description": "An enumeration.", + "enum": [ + "analysis", + "coverage", + "crashes", + "inputs", + "no_repro", + "readonly_inputs", + "reports", + "setup", + "tools", + "unique_inputs", + "unique_reports", + "regression_reports" + ], + "title": "ContainerType" + }, + "StatsFormat": { + "description": "An enumeration.", + "enum": [ + "AFL" + ], + "title": "StatsFormat" + }, + "TaskConfig": { + "properties": { + "colocate": { + "title": "Colocate", + "type": "boolean" + }, + "containers": { + "items": { + "$ref": "#/definitions/TaskContainers" + }, + "title": "Containers", + "type": "array" + }, + "debug": { + "items": { + "$ref": "#/definitions/TaskDebugFlag" + }, + "type": "array" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "pool": { + "$ref": "#/definitions/TaskPool" + }, + "prereq_tasks": { + "items": { + "format": "uuid", + "type": "string" + }, + "title": "Prereq Tasks", + "type": "array" + }, + "tags": { + "additionalProperties": { + "type": "string" + }, + "title": "Tags", + "type": "object" + }, + "task": { + "$ref": "#/definitions/TaskDetails" + }, + "vm": { + "$ref": "#/definitions/TaskVm" + } + }, + "required": [ + "job_id", + "task", + "containers", + "tags" + ], + "title": "TaskConfig", + "type": "object" + }, + "TaskContainers": { + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "type": { + "$ref": "#/definitions/ContainerType" + } + }, + "required": [ + "type", + "name" + ], + "title": "TaskContainers", + "type": "object" + }, + "TaskDebugFlag": { + "description": "An enumeration.", + "enum": [ + "keep_node_on_failure", + "keep_node_on_completion" + ], + "title": "TaskDebugFlag" + }, + "TaskDetails": { + "properties": { + "analyzer_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Analyzer Env", + "type": "object" + }, + "analyzer_exe": { + "title": "Analyzer Exe", + "type": "string" + }, + "analyzer_options": { + "items": { + "type": "string" + }, + "title": "Analyzer Options", + "type": "array" + }, + "check_asan_log": { + "title": "Check Asan Log", + "type": "boolean" + }, + "check_debugger": { + "default": true, + "title": "Check Debugger", + "type": "boolean" + }, + "check_fuzzer_help": { + "title": "Check Fuzzer Help", + "type": "boolean" + }, + "check_retry_count": { + "title": "Check Retry Count", + "type": "integer" + }, + "duration": { + "title": "Duration", + "type": "integer" + }, + "ensemble_sync_delay": { + "title": "Ensemble Sync Delay", + "type": "integer" + }, + "expect_crash_on_failure": { + "title": "Expect Crash On Failure", + "type": "boolean" + }, + "generator_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Generator Env", + "type": "object" + }, + "generator_exe": { + "title": "Generator Exe", + "type": "string" + }, + "generator_options": { + "items": { + "type": "string" + }, + "title": "Generator Options", + "type": "array" + }, + "minimized_stack_depth": { + "title": "Minimized Stack Depth", + "type": "integer" + }, + "preserve_existing_outputs": { + "title": "Preserve Existing Outputs", + "type": "boolean" + }, + "reboot_after_setup": { + "title": "Reboot After Setup", + "type": "boolean" + }, + "rename_output": { + "title": "Rename Output", + "type": "boolean" + }, + "report_list": { + "items": { + "type": "string" + }, + "title": "Report List", + "type": "array" + }, + "stats_file": { + "title": "Stats File", + "type": "string" + }, + "stats_format": { + "$ref": "#/definitions/StatsFormat" + }, + "supervisor_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Supervisor Env", + "type": "object" + }, + "supervisor_exe": { + "title": "Supervisor Exe", + "type": "string" + }, + "supervisor_input_marker": { + "title": "Supervisor Input Marker", + "type": "string" + }, + "supervisor_options": { + "items": { + "type": "string" + }, + "title": "Supervisor Options", + "type": "array" + }, + "target_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Target Env", + "type": "object" + }, + "target_exe": { + "title": "Target Exe", + "type": "string" + }, + "target_options": { + "items": { + "type": "string" + }, + "title": "Target Options", + "type": "array" + }, + "target_options_merge": { + "title": "Target Options Merge", + "type": "boolean" + }, + "target_timeout": { + "title": "Target Timeout", + "type": "integer" + }, + "target_workers": { + "title": "Target Workers", + "type": "integer" + }, + "type": { + "$ref": "#/definitions/TaskType" + }, + "wait_for_files": { + "$ref": "#/definitions/ContainerType" + } + }, + "required": [ + "type", + "duration" + ], + "title": "TaskDetails", + "type": "object" + }, + "TaskPool": { + "properties": { + "count": { + "title": "Count", + "type": "integer" + }, + "pool_name": { + "title": "Pool Name", + "type": "string" + } + }, + "required": [ + "count", + "pool_name" + ], + "title": "TaskPool", + "type": "object" + }, + "TaskType": { + "description": "An enumeration.", + "enum": [ + "libfuzzer_fuzz", + "libfuzzer_coverage", + "libfuzzer_crash_report", + "libfuzzer_merge", + "libfuzzer_regression", + "generic_analysis", + "generic_supervisor", + "generic_merge", + "generic_generator", + "generic_crash_report", + "generic_regression" + ], + "title": "TaskType" + }, + "TaskVm": { + "properties": { + "count": { + "default": 1, + "title": "Count", + "type": "integer" + }, + "image": { + "title": "Image", + "type": "string" + }, + "reboot_after_setup": { + "title": "Reboot After Setup", + "type": "boolean" + }, + "region": { + "title": "Region", + "type": "string" + }, + "sku": { + "title": "Sku", + "type": "string" + }, + "spot_instances": { + "default": false, + "title": "Spot Instances", + "type": "boolean" + } + }, + "required": [ + "region", + "sku", + "image" + ], + "title": "TaskVm", + "type": "object" + }, + "UserInfo": { + "properties": { + "application_id": { + "format": "uuid", + "title": "Application Id", + "type": "string" + }, + "object_id": { + "format": "uuid", + "title": "Object Id", + "type": "string" + }, + "upn": { + "title": "Upn", + "type": "string" + } + }, + "title": "UserInfo", + "type": "object" + } + }, + "properties": { + "config": { + "$ref": "#/definitions/TaskConfig" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "task_id": { + "format": "uuid", + "title": "Task Id", + "type": "string" + }, + "user_info": { + "$ref": "#/definitions/UserInfo" + } + }, + "required": [ + "job_id", + "task_id", + "config" + ], + "title": "EventTaskStopped", + "type": "object" +} +``` + +## Full Event Schema + +```json +{ + "definitions": { + "Architecture": { + "description": "An enumeration.", + "enum": [ + "x86_64" + ], + "title": "Architecture" + }, + "AutoScaleConfig": { + "properties": { + "ephemeral_os_disks": { + "default": false, + "title": "Ephemeral Os Disks", + "type": "boolean" + }, + "image": { + "title": "Image", + "type": "string" + }, + "max_size": { + "title": "Max Size", + "type": "integer" + }, + "min_size": { + "default": 0, + "title": "Min Size", + "type": "integer" + }, + "region": { + "title": "Region", + "type": "string" + }, + "scaleset_size": { + "title": "Scaleset Size", + "type": "integer" + }, + "spot_instances": { + "default": false, + "title": "Spot Instances", + "type": "boolean" + }, + "vm_sku": { + "title": "Vm Sku", + "type": "string" + } + }, + "required": [ + "image", + "scaleset_size", + "vm_sku" + ], + "title": "AutoScaleConfig", + "type": "object" + }, + "BlobRef": { + "properties": { + "account": { + "title": "Account", + "type": "string" + }, + "container": { + "title": "Container", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + } + }, + "required": [ + "account", + "container", + "name" + ], + "title": "BlobRef", + "type": "object" + }, + "ContainerType": { + "description": "An enumeration.", + "enum": [ + "analysis", + "coverage", + "crashes", + "inputs", + "no_repro", + "readonly_inputs", + "reports", + "setup", + "tools", + "unique_inputs", + "unique_reports", + "regression_reports" + ], + "title": "ContainerType" + }, + "CrashTestResult": { + "properties": { + "crash_report": { + "$ref": "#/definitions/Report" + }, + "no_repro": { + "$ref": "#/definitions/NoReproReport" + } + }, + "title": "CrashTestResult", + "type": "object" + }, + "Error": { + "properties": { + "code": { + "$ref": "#/definitions/ErrorCode" + }, + "errors": { + "items": { + "type": "string" + }, + "title": "Errors", + "type": "array" + } + }, + "required": [ + "code", + "errors" + ], + "title": "Error", + "type": "object" + }, + "ErrorCode": { + "description": "An enumeration.", + "enum": [ + 450, + 451, + 452, + 453, + 454, + 455, + 456, + 457, + 458, + 459, + 460, + 461, + 462, + 463, + 464, + 465, + 467, + 468, + 469, + 470, + 471, + 472 + ], + "title": "ErrorCode" + }, + "EventCrashReported": { + "properties": { + "container": { + "title": "Container", + "type": "string" + }, + "filename": { + "title": "Filename", + "type": "string" + }, + "report": { + "$ref": "#/definitions/Report" + }, + "task_config": { + "$ref": "#/definitions/TaskConfig" + } + }, + "required": [ + "report", + "container", + "filename" + ], + "title": "EventCrashReported", + "type": "object" + }, + "EventFileAdded": { + "properties": { + "container": { + "title": "Container", + "type": "string" + }, + "filename": { + "title": "Filename", + "type": "string" + } + }, + "required": [ + "container", + "filename" + ], + "title": "EventFileAdded", + "type": "object" + }, + "EventJobCreated": { + "properties": { + "config": { + "$ref": "#/definitions/JobConfig" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "user_info": { + "$ref": "#/definitions/UserInfo" + } + }, + "required": [ + "job_id", + "config" + ], + "title": "EventJobCreated", + "type": "object" + }, + "EventJobStopped": { + "properties": { + "config": { + "$ref": "#/definitions/JobConfig" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "task_info": { + "items": { + "$ref": "#/definitions/JobTaskStopped" + }, + "title": "Task Info", + "type": "array" + }, + "user_info": { + "$ref": "#/definitions/UserInfo" + } + }, + "required": [ + "job_id", + "config" + ], + "title": "EventJobStopped", + "type": "object" + }, + "EventNodeCreated": { + "properties": { + "machine_id": { + "format": "uuid", + "title": "Machine Id", + "type": "string" + }, + "pool_name": { + "title": "Pool Name", + "type": "string" + }, + "scaleset_id": { + "format": "uuid", + "title": "Scaleset Id", + "type": "string" + } + }, + "required": [ + "machine_id", + "pool_name" + ], + "title": "EventNodeCreated", + "type": "object" + }, + "EventNodeDeleted": { + "properties": { + "machine_id": { + "format": "uuid", + "title": "Machine Id", + "type": "string" + }, + "pool_name": { + "title": "Pool Name", + "type": "string" + }, + "scaleset_id": { + "format": "uuid", + "title": "Scaleset Id", + "type": "string" + } + }, + "required": [ + "machine_id", + "pool_name" + ], + "title": "EventNodeDeleted", + "type": "object" + }, + "EventNodeHeartbeat": { + "properties": { + "machine_id": { + "format": "uuid", + "title": "Machine Id", + "type": "string" + }, + "pool_name": { + "title": "Pool Name", + "type": "string" + }, + "scaleset_id": { + "format": "uuid", + "title": "Scaleset Id", + "type": "string" + } + }, + "required": [ + "machine_id", + "pool_name" + ], + "title": "EventNodeHeartbeat", + "type": "object" + }, + "EventNodeStateUpdated": { + "properties": { + "machine_id": { + "format": "uuid", + "title": "Machine Id", + "type": "string" + }, + "pool_name": { + "title": "Pool Name", + "type": "string" + }, + "scaleset_id": { + "format": "uuid", + "title": "Scaleset Id", + "type": "string" + }, + "state": { + "$ref": "#/definitions/NodeState" + } + }, + "required": [ + "machine_id", + "pool_name", + "state" + ], + "title": "EventNodeStateUpdated", + "type": "object" + }, + "EventPing": { + "properties": { + "ping_id": { + "format": "uuid", + "title": "Ping Id", + "type": "string" + } + }, + "required": [ + "ping_id" + ], + "title": "EventPing", + "type": "object" + }, + "EventPoolCreated": { + "properties": { + "arch": { + "$ref": "#/definitions/Architecture" + }, + "autoscale": { + "$ref": "#/definitions/AutoScaleConfig" + }, + "managed": { + "title": "Managed", + "type": "boolean" + }, + "os": { + "$ref": "#/definitions/OS" + }, + "pool_name": { + "title": "Pool Name", + "type": "string" + } + }, + "required": [ + "pool_name", + "os", + "arch", + "managed" + ], + "title": "EventPoolCreated", + "type": "object" + }, + "EventPoolDeleted": { + "properties": { + "pool_name": { + "title": "Pool Name", + "type": "string" + } + }, + "required": [ + "pool_name" + ], + "title": "EventPoolDeleted", + "type": "object" + }, + "EventProxyCreated": { + "properties": { + "proxy_id": { + "format": "uuid", + "title": "Proxy Id", + "type": "string" + }, + "region": { + "title": "Region", + "type": "string" + } + }, + "required": [ + "region", + "proxy_id" + ], + "title": "EventProxyCreated", + "type": "object" + }, + "EventProxyDeleted": { + "properties": { + "proxy_id": { + "format": "uuid", + "title": "Proxy Id", + "type": "string" + }, + "region": { + "title": "Region", + "type": "string" + } + }, + "required": [ + "region", + "proxy_id" + ], + "title": "EventProxyDeleted", + "type": "object" + }, + "EventProxyFailed": { + "properties": { + "error": { + "$ref": "#/definitions/Error" + }, + "proxy_id": { + "format": "uuid", + "title": "Proxy Id", + "type": "string" + }, + "region": { + "title": "Region", + "type": "string" + } + }, + "required": [ + "region", + "proxy_id", + "error" + ], + "title": "EventProxyFailed", + "type": "object" + }, + "EventRegressionReported": { + "properties": { + "container": { + "title": "Container", + "type": "string" + }, + "filename": { + "title": "Filename", + "type": "string" + }, + "regression_report": { + "$ref": "#/definitions/RegressionReport" + }, + "task_config": { + "$ref": "#/definitions/TaskConfig" + } + }, + "required": [ + "regression_report", + "container", + "filename" + ], + "title": "EventRegressionReported", + "type": "object" + }, + "EventScalesetCreated": { + "properties": { + "image": { + "title": "Image", + "type": "string" + }, + "pool_name": { + "title": "Pool Name", + "type": "string" + }, + "region": { + "title": "Region", + "type": "string" + }, + "scaleset_id": { + "format": "uuid", + "title": "Scaleset Id", + "type": "string" + }, + "size": { + "title": "Size", + "type": "integer" + }, + "vm_sku": { + "title": "Vm Sku", + "type": "string" + } + }, + "required": [ + "scaleset_id", + "pool_name", + "vm_sku", + "image", + "region", + "size" + ], + "title": "EventScalesetCreated", + "type": "object" + }, + "EventScalesetDeleted": { + "properties": { + "pool_name": { + "title": "Pool Name", + "type": "string" + }, + "scaleset_id": { + "format": "uuid", + "title": "Scaleset Id", + "type": "string" + } + }, + "required": [ + "scaleset_id", + "pool_name" + ], + "title": "EventScalesetDeleted", + "type": "object" + }, + "EventScalesetFailed": { + "properties": { + "error": { + "$ref": "#/definitions/Error" + }, + "pool_name": { + "title": "Pool Name", + "type": "string" + }, + "scaleset_id": { + "format": "uuid", + "title": "Scaleset Id", + "type": "string" + } + }, + "required": [ + "scaleset_id", + "pool_name", + "error" + ], + "title": "EventScalesetFailed", + "type": "object" + }, + "EventTaskCreated": { + "properties": { + "config": { + "$ref": "#/definitions/TaskConfig" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "task_id": { + "format": "uuid", + "title": "Task Id", + "type": "string" + }, + "user_info": { + "$ref": "#/definitions/UserInfo" + } + }, + "required": [ + "job_id", + "task_id", + "config" + ], + "title": "EventTaskCreated", + "type": "object" + }, + "EventTaskFailed": { + "properties": { + "config": { + "$ref": "#/definitions/TaskConfig" + }, + "error": { + "$ref": "#/definitions/Error" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "task_id": { + "format": "uuid", + "title": "Task Id", + "type": "string" + }, + "user_info": { + "$ref": "#/definitions/UserInfo" + } + }, + "required": [ + "job_id", + "task_id", + "error", + "config" + ], + "title": "EventTaskFailed", + "type": "object" + }, + "EventTaskHeartbeat": { + "properties": { + "config": { + "$ref": "#/definitions/TaskConfig" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "task_id": { + "format": "uuid", + "title": "Task Id", + "type": "string" + } + }, + "required": [ + "job_id", + "task_id", + "config" + ], + "title": "EventTaskHeartbeat", + "type": "object" + }, + "EventTaskStateUpdated": { + "properties": { + "config": { + "$ref": "#/definitions/TaskConfig" + }, + "end_time": { + "format": "date-time", + "title": "End Time", + "type": "string" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "state": { + "$ref": "#/definitions/TaskState" + }, + "task_id": { + "format": "uuid", + "title": "Task Id", + "type": "string" + } + }, + "required": [ + "job_id", + "task_id", + "state", + "config" + ], + "title": "EventTaskStateUpdated", + "type": "object" + }, + "EventTaskStopped": { + "properties": { + "config": { + "$ref": "#/definitions/TaskConfig" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "task_id": { + "format": "uuid", + "title": "Task Id", + "type": "string" + }, + "user_info": { + "$ref": "#/definitions/UserInfo" + } + }, + "required": [ + "job_id", + "task_id", + "config" + ], + "title": "EventTaskStopped", + "type": "object" + }, + "EventType": { + "description": "An enumeration.", + "enum": [ + "job_created", + "job_stopped", + "node_created", + "node_deleted", + "node_state_updated", + "ping", + "pool_created", + "pool_deleted", + "proxy_created", + "proxy_deleted", + "proxy_failed", + "scaleset_created", + "scaleset_deleted", + "scaleset_failed", + "task_created", + "task_failed", + "task_state_updated", + "task_stopped", + "crash_reported", + "regression_reported", + "file_added", + "task_heartbeat", + "node_heartbeat" + ], + "title": "EventType" + }, + "JobConfig": { + "properties": { + "build": { + "title": "Build", + "type": "string" + }, + "duration": { + "title": "Duration", + "type": "integer" + }, + "name": { + "title": "Name", + "type": "string" + }, + "project": { + "title": "Project", + "type": "string" + } + }, + "required": [ + "project", + "name", + "build", + "duration" + ], + "title": "JobConfig", + "type": "object" + }, + "JobTaskStopped": { + "properties": { + "error": { + "$ref": "#/definitions/Error" + }, + "task_id": { + "format": "uuid", + "title": "Task Id", + "type": "string" + }, + "task_type": { + "$ref": "#/definitions/TaskType" + } + }, + "required": [ + "task_id", + "task_type" + ], + "title": "JobTaskStopped", + "type": "object" + }, + "NoReproReport": { + "properties": { + "error": { + "title": "Error", + "type": "string" + }, + "executable": { + "title": "Executable", + "type": "string" + }, + "input_blob": { + "$ref": "#/definitions/BlobRef" + }, + "input_sha256": { + "title": "Input Sha256", + "type": "string" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "task_id": { + "format": "uuid", + "title": "Task Id", + "type": "string" + }, + "tries": { + "title": "Tries", + "type": "integer" + } + }, + "required": [ + "input_sha256", + "executable", + "task_id", + "job_id", + "tries" + ], + "title": "NoReproReport", + "type": "object" + }, + "NodeState": { + "description": "An enumeration.", + "enum": [ + "init", + "free", + "setting_up", + "rebooting", + "ready", + "busy", + "done", + "shutdown", + "halt" + ], + "title": "NodeState" + }, + "OS": { + "description": "An enumeration.", + "enum": [ + "windows", + "linux" + ], + "title": "OS" + }, + "RegressionReport": { + "properties": { + "crash_test_result": { + "$ref": "#/definitions/CrashTestResult" + }, + "original_crash_test_result": { + "$ref": "#/definitions/CrashTestResult" + } + }, + "required": [ + "crash_test_result" + ], + "title": "RegressionReport", + "type": "object" + }, + "Report": { + "properties": { + "asan_log": { + "title": "Asan Log", + "type": "string" + }, + "call_stack": { + "items": { + "type": "string" + }, + "title": "Call Stack", + "type": "array" + }, + "call_stack_sha256": { + "title": "Call Stack Sha256", + "type": "string" + }, + "crash_site": { + "title": "Crash Site", + "type": "string" + }, + "crash_type": { + "title": "Crash Type", + "type": "string" + }, + "executable": { + "title": "Executable", + "type": "string" + }, + "input_blob": { + "$ref": "#/definitions/BlobRef" + }, + "input_sha256": { + "title": "Input Sha256", + "type": "string" + }, + "input_url": { + "title": "Input Url", + "type": "string" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "minimized_stack": { + "items": { + "type": "string" + }, + "title": "Minimized Stack", + "type": "array" + }, + "minimized_stack_function_names": { + "items": { + "type": "string" + }, + "title": "Minimized Stack Function Names", + "type": "array" + }, + "minimized_stack_function_names_sha256": { + "title": "Minimized Stack Function Names Sha256", + "type": "string" + }, + "minimized_stack_sha256": { + "title": "Minimized Stack Sha256", + "type": "string" + }, + "scariness_description": { + "title": "Scariness Description", + "type": "string" + }, + "scariness_score": { + "title": "Scariness Score", + "type": "integer" + }, + "task_id": { + "format": "uuid", + "title": "Task Id", + "type": "string" + } + }, + "required": [ + "executable", + "crash_type", + "crash_site", + "call_stack", + "call_stack_sha256", + "input_sha256", + "task_id", + "job_id" + ], + "title": "Report", + "type": "object" + }, + "StatsFormat": { + "description": "An enumeration.", + "enum": [ + "AFL" + ], + "title": "StatsFormat" + }, + "TaskConfig": { + "properties": { + "colocate": { + "title": "Colocate", + "type": "boolean" + }, + "containers": { + "items": { + "$ref": "#/definitions/TaskContainers" + }, + "title": "Containers", + "type": "array" + }, + "debug": { + "items": { + "$ref": "#/definitions/TaskDebugFlag" + }, + "type": "array" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "pool": { + "$ref": "#/definitions/TaskPool" + }, + "prereq_tasks": { + "items": { + "format": "uuid", + "type": "string" + }, + "title": "Prereq Tasks", + "type": "array" + }, + "tags": { + "additionalProperties": { + "type": "string" + }, + "title": "Tags", + "type": "object" + }, + "task": { + "$ref": "#/definitions/TaskDetails" + }, + "vm": { + "$ref": "#/definitions/TaskVm" + } + }, + "required": [ + "job_id", + "task", + "containers", + "tags" + ], + "title": "TaskConfig", + "type": "object" + }, + "TaskContainers": { + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "type": { + "$ref": "#/definitions/ContainerType" + } + }, + "required": [ + "type", + "name" + ], + "title": "TaskContainers", + "type": "object" + }, + "TaskDebugFlag": { + "description": "An enumeration.", + "enum": [ + "keep_node_on_failure", + "keep_node_on_completion" + ], + "title": "TaskDebugFlag" + }, + "TaskDetails": { + "properties": { + "analyzer_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Analyzer Env", + "type": "object" + }, + "analyzer_exe": { + "title": "Analyzer Exe", + "type": "string" + }, + "analyzer_options": { + "items": { + "type": "string" + }, + "title": "Analyzer Options", + "type": "array" + }, + "check_asan_log": { + "title": "Check Asan Log", + "type": "boolean" + }, + "check_debugger": { + "default": true, + "title": "Check Debugger", + "type": "boolean" + }, + "check_fuzzer_help": { + "title": "Check Fuzzer Help", + "type": "boolean" + }, + "check_retry_count": { + "title": "Check Retry Count", + "type": "integer" + }, + "duration": { + "title": "Duration", + "type": "integer" + }, + "ensemble_sync_delay": { + "title": "Ensemble Sync Delay", + "type": "integer" + }, + "expect_crash_on_failure": { + "title": "Expect Crash On Failure", + "type": "boolean" + }, + "generator_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Generator Env", + "type": "object" + }, + "generator_exe": { + "title": "Generator Exe", + "type": "string" + }, + "generator_options": { + "items": { + "type": "string" + }, + "title": "Generator Options", + "type": "array" + }, + "minimized_stack_depth": { + "title": "Minimized Stack Depth", + "type": "integer" + }, + "preserve_existing_outputs": { + "title": "Preserve Existing Outputs", + "type": "boolean" + }, + "reboot_after_setup": { + "title": "Reboot After Setup", + "type": "boolean" + }, + "rename_output": { + "title": "Rename Output", + "type": "boolean" + }, + "report_list": { + "items": { + "type": "string" + }, + "title": "Report List", + "type": "array" + }, + "stats_file": { + "title": "Stats File", + "type": "string" + }, + "stats_format": { + "$ref": "#/definitions/StatsFormat" + }, + "supervisor_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Supervisor Env", + "type": "object" + }, + "supervisor_exe": { + "title": "Supervisor Exe", + "type": "string" + }, + "supervisor_input_marker": { + "title": "Supervisor Input Marker", + "type": "string" + }, + "supervisor_options": { + "items": { + "type": "string" + }, + "title": "Supervisor Options", + "type": "array" + }, + "target_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Target Env", + "type": "object" + }, + "target_exe": { + "title": "Target Exe", + "type": "string" + }, + "target_options": { + "items": { + "type": "string" + }, + "title": "Target Options", + "type": "array" + }, + "target_options_merge": { + "title": "Target Options Merge", + "type": "boolean" + }, + "target_timeout": { + "title": "Target Timeout", + "type": "integer" + }, + "target_workers": { + "title": "Target Workers", + "type": "integer" + }, + "type": { + "$ref": "#/definitions/TaskType" + }, + "wait_for_files": { + "$ref": "#/definitions/ContainerType" + } + }, + "required": [ + "type", + "duration" + ], + "title": "TaskDetails", + "type": "object" + }, + "TaskPool": { + "properties": { + "count": { + "title": "Count", + "type": "integer" + }, + "pool_name": { + "title": "Pool Name", + "type": "string" + } + }, + "required": [ + "count", + "pool_name" + ], + "title": "TaskPool", + "type": "object" + }, + "TaskState": { + "description": "An enumeration.", + "enum": [ + "init", + "waiting", + "scheduled", + "setting_up", + "running", + "stopping", + "stopped", + "wait_job" + ], + "title": "TaskState" + }, + "TaskType": { + "description": "An enumeration.", + "enum": [ + "libfuzzer_fuzz", + "libfuzzer_coverage", + "libfuzzer_crash_report", + "libfuzzer_merge", + "libfuzzer_regression", + "generic_analysis", + "generic_supervisor", + "generic_merge", + "generic_generator", + "generic_crash_report", + "generic_regression" + ], + "title": "TaskType" + }, + "TaskVm": { + "properties": { + "count": { + "default": 1, + "title": "Count", + "type": "integer" + }, + "image": { + "title": "Image", + "type": "string" + }, + "reboot_after_setup": { + "title": "Reboot After Setup", + "type": "boolean" + }, + "region": { + "title": "Region", + "type": "string" + }, + "sku": { + "title": "Sku", + "type": "string" + }, + "spot_instances": { + "default": false, + "title": "Spot Instances", + "type": "boolean" + } + }, + "required": [ + "region", + "sku", + "image" + ], + "title": "TaskVm", + "type": "object" + }, + "UserInfo": { + "properties": { + "application_id": { + "format": "uuid", + "title": "Application Id", + "type": "string" + }, + "object_id": { + "format": "uuid", + "title": "Object Id", + "type": "string" + }, + "upn": { + "title": "Upn", + "type": "string" + } + }, + "title": "UserInfo", + "type": "object" + } + }, + "properties": { + "event": { + "anyOf": [ + { + "$ref": "#/definitions/EventJobCreated" + }, + { + "$ref": "#/definitions/EventJobStopped" + }, + { + "$ref": "#/definitions/EventNodeStateUpdated" + }, + { + "$ref": "#/definitions/EventNodeCreated" + }, + { + "$ref": "#/definitions/EventNodeDeleted" + }, + { + "$ref": "#/definitions/EventNodeHeartbeat" + }, + { + "$ref": "#/definitions/EventPing" + }, + { + "$ref": "#/definitions/EventPoolCreated" + }, + { + "$ref": "#/definitions/EventPoolDeleted" + }, + { + "$ref": "#/definitions/EventProxyFailed" + }, + { + "$ref": "#/definitions/EventProxyCreated" + }, + { + "$ref": "#/definitions/EventProxyDeleted" + }, + { + "$ref": "#/definitions/EventScalesetFailed" + }, + { + "$ref": "#/definitions/EventScalesetCreated" + }, + { + "$ref": "#/definitions/EventScalesetDeleted" + }, + { + "$ref": "#/definitions/EventTaskFailed" + }, + { + "$ref": "#/definitions/EventTaskStateUpdated" + }, + { + "$ref": "#/definitions/EventTaskCreated" + }, + { + "$ref": "#/definitions/EventTaskStopped" + }, + { + "$ref": "#/definitions/EventTaskHeartbeat" + }, + { + "$ref": "#/definitions/EventCrashReported" + }, + { + "$ref": "#/definitions/EventRegressionReported" + }, + { + "$ref": "#/definitions/EventFileAdded" + } + ], + "title": "Event" + }, + "event_id": { + "format": "uuid", + "title": "Event Id", + "type": "string" + }, + "event_type": { + "$ref": "#/definitions/EventType" + }, + "instance_id": { + "format": "uuid", + "title": "Instance Id", + "type": "string" + }, + "instance_name": { + "title": "Instance Name", + "type": "string" + }, + "webhook_id": { + "format": "uuid", + "title": "Webhook Id", + "type": "string" + } + }, + "required": [ + "event_type", + "event", + "instance_id", + "instance_name", + "webhook_id" + ], + "title": "WebhookMessage", + "type": "object" +} +``` + diff --git a/src/pytypes/extra/generate-docs.py b/src/pytypes/extra/generate-docs.py index 475436f5eb..32312965e5 100755 --- a/src/pytypes/extra/generate-docs.py +++ b/src/pytypes/extra/generate-docs.py @@ -64,10 +64,30 @@ EMPTY_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" ZERO_SHA256 = "0" * len(EMPTY_SHA256) -OUTPUT_FILE_DIR = "./webhook_events.md" -OUTPUT_FILE = open(OUTPUT_FILE_DIR, "w", newline="\n", encoding="ascii") +def generate_file(): + outputfilename = "webhook_events.md" + outputfilepath = "./" + try: + opts, args = getopt(argv[1:], "hi:o:", ["ifile=", "ofile="]) + except GetoptError: + print("Incorrect command line arguments: generate-docs.py -o ") + exit(2) + for opt, arg in opts: + if opt == "-h": + print("Proper command line arguments: generate-docs.py -o ") + exit() + elif opt in ("-o", "--ofile"): + outputfilepath = arg + + outputfiledir = outputfilepath + outputfilename + append_write = "w" + print("Output file is ", outputfiledir) + return open(outputfiledir, append_write, newline="\n", encoding="ascii") + +OUTPUT_FILE = generate_file() + def layer(depth: int, title: str, content: Optional[str] = None) -> None: print(f"{'#' * depth} {title}\n") OUTPUT_FILE.write(f"{'#' * depth} {title}\n") @@ -85,31 +105,7 @@ def typed(depth: int, title: str, content: str, data_type: str) -> None: def main() -> None: - - outputfilename = "webhook_events.md" - outputfilepath = "./" - try: - opts, args = getopt(argv[1:], "hi:o:", ["ifile=", "ofile="]) - except GetoptError: - print("Incorrect command line arguments: generate-docs.py -o ") - exit(2) - for opt, arg in opts: - if opt == "-h": - print("Proper command line arguments: generate-docs.py -o ") - exit() - elif opt in ("-o", "--ofile"): - outputfilepath = arg - - OUTPUT_FILE_DIR = outputfilepath + outputfilename - append_write = "x" - if path.exists(OUTPUT_FILE_DIR): - append_write = "a" - else: - append_write = "w" - - print("Output file is ", OUTPUT_FILE_DIR) - OUTPUT_FILE = open(OUTPUT_FILE_DIR, append_write, newline="\n", encoding="ascii") - + generate_file() task_config = TaskConfig( job_id=UUID(int=0), task=TaskDetails( From dd67da19383ec464ef9064ccaa7078a6747eb202 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 11 May 2021 16:35:21 -0700 Subject: [PATCH 053/110] isort and black --- src/pytypes/extra/generate-docs.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pytypes/extra/generate-docs.py b/src/pytypes/extra/generate-docs.py index 32312965e5..542ec33327 100755 --- a/src/pytypes/extra/generate-docs.py +++ b/src/pytypes/extra/generate-docs.py @@ -65,6 +65,7 @@ EMPTY_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" ZERO_SHA256 = "0" * len(EMPTY_SHA256) + def generate_file(): outputfilename = "webhook_events.md" outputfilepath = "./" @@ -86,8 +87,10 @@ def generate_file(): print("Output file is ", outputfiledir) return open(outputfiledir, append_write, newline="\n", encoding="ascii") + OUTPUT_FILE = generate_file() - + + def layer(depth: int, title: str, content: Optional[str] = None) -> None: print(f"{'#' * depth} {title}\n") OUTPUT_FILE.write(f"{'#' * depth} {title}\n") From f114f5ea85024289f0d69f863646fa4ef14e9583 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 11 May 2021 16:36:31 -0700 Subject: [PATCH 054/110] Removing old imports. --- src/pytypes/extra/generate-docs.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pytypes/extra/generate-docs.py b/src/pytypes/extra/generate-docs.py index 542ec33327..aa43759de5 100755 --- a/src/pytypes/extra/generate-docs.py +++ b/src/pytypes/extra/generate-docs.py @@ -4,7 +4,6 @@ # Licensed under the MIT License. from getopt import GetoptError, getopt -from os import path from sys import argv, exit from typing import List, Optional from uuid import UUID From 4f01c8335405e737272fc8296a2273a30584ff15 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 11 May 2021 16:45:46 -0700 Subject: [PATCH 055/110] Formatting. --- src/pytypes/extra/generate-docs.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/pytypes/extra/generate-docs.py b/src/pytypes/extra/generate-docs.py index aa43759de5..f7b7d9ec9e 100755 --- a/src/pytypes/extra/generate-docs.py +++ b/src/pytypes/extra/generate-docs.py @@ -65,7 +65,7 @@ ZERO_SHA256 = "0" * len(EMPTY_SHA256) -def generate_file(): +def generate_file() -> str: outputfilename = "webhook_events.md" outputfilepath = "./" try: @@ -81,13 +81,12 @@ def generate_file(): outputfilepath = arg outputfiledir = outputfilepath + outputfilename - append_write = "w" print("Output file is ", outputfiledir) - return open(outputfiledir, append_write, newline="\n", encoding="ascii") + return outputfiledir -OUTPUT_FILE = generate_file() +OUTPUT_FILE = open(generate_file(), "w", newline="\n", encoding="ascii") def layer(depth: int, title: str, content: Optional[str] = None) -> None: From 96bf458df1e600d48c377ddf5860dc0b84f4cbc7 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 11 May 2021 17:31:26 -0700 Subject: [PATCH 056/110] Trying old version of script. --- src/pytypes/extra/generate-docs.py | 37 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/pytypes/extra/generate-docs.py b/src/pytypes/extra/generate-docs.py index f7b7d9ec9e..df3f6c57c0 100755 --- a/src/pytypes/extra/generate-docs.py +++ b/src/pytypes/extra/generate-docs.py @@ -65,28 +65,28 @@ ZERO_SHA256 = "0" * len(EMPTY_SHA256) -def generate_file() -> str: - outputfilename = "webhook_events.md" - outputfilepath = "./" - try: - opts, args = getopt(argv[1:], "hi:o:", ["ifile=", "ofile="]) - except GetoptError: - print("Incorrect command line arguments: generate-docs.py -o ") - exit(2) - for opt, arg in opts: - if opt == "-h": - print("Proper command line arguments: generate-docs.py -o ") - exit() - elif opt in ("-o", "--ofile"): - outputfilepath = arg +# def generate_file() -> str: +# outputfilename = "webhook_events.md" +# outputfilepath = "./" +# try: +# opts, args = getopt(argv[1:], "hi:o:", ["ifile=", "ofile="]) +# except GetoptError: +# print("Incorrect command line arguments: generate-docs.py -o ") +# exit(2) +# for opt, arg in opts: +# if opt == "-h": +# print("Proper command line arguments: generate-docs.py -o ") +# exit() +# elif opt in ("-o", "--ofile"): +# outputfilepath = arg - outputfiledir = outputfilepath + outputfilename +# outputfiledir = outputfilepath + outputfilename - print("Output file is ", outputfiledir) - return outputfiledir +# print("Output file is ", outputfiledir) +# return outputfiledir -OUTPUT_FILE = open(generate_file(), "w", newline="\n", encoding="ascii") +OUTPUT_FILE = open('../../../docs/webhook_events.md', 'w', newline='\n', encoding='ascii') def layer(depth: int, title: str, content: Optional[str] = None) -> None: @@ -106,7 +106,6 @@ def typed(depth: int, title: str, content: str, data_type: str) -> None: def main() -> None: - generate_file() task_config = TaskConfig( job_id=UUID(int=0), task=TaskDetails( From 5fb226c44d9735f7b6bfb0eaf89bf72f9b97ca84 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 11 May 2021 17:50:54 -0700 Subject: [PATCH 057/110] Fixing generate docs and shell script. --- src/ci/onefuzztypes.sh | 4 ++-- src/pytypes/extra/generate-docs.py | 37 +++++++++++++++--------------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/ci/onefuzztypes.sh b/src/ci/onefuzztypes.sh index 49a5faed1b..1d085e9ec6 100755 --- a/src/ci/onefuzztypes.sh +++ b/src/ci/onefuzztypes.sh @@ -25,7 +25,7 @@ echo 'verify webhook docs are up-to-date' python -m venv build-docs . build-docs/bin/activate pip install -e . -python extra/generate-docs.py > ../../docs/webhook_events.md -git diff --quiet ../../docs/webhook_events.md +python extra/generate-docs.py -o "../../docs/" +git diff ../../docs/webhook_events.md deactivate rm -rf build-docs diff --git a/src/pytypes/extra/generate-docs.py b/src/pytypes/extra/generate-docs.py index df3f6c57c0..f7b7d9ec9e 100755 --- a/src/pytypes/extra/generate-docs.py +++ b/src/pytypes/extra/generate-docs.py @@ -65,28 +65,28 @@ ZERO_SHA256 = "0" * len(EMPTY_SHA256) -# def generate_file() -> str: -# outputfilename = "webhook_events.md" -# outputfilepath = "./" -# try: -# opts, args = getopt(argv[1:], "hi:o:", ["ifile=", "ofile="]) -# except GetoptError: -# print("Incorrect command line arguments: generate-docs.py -o ") -# exit(2) -# for opt, arg in opts: -# if opt == "-h": -# print("Proper command line arguments: generate-docs.py -o ") -# exit() -# elif opt in ("-o", "--ofile"): -# outputfilepath = arg +def generate_file() -> str: + outputfilename = "webhook_events.md" + outputfilepath = "./" + try: + opts, args = getopt(argv[1:], "hi:o:", ["ifile=", "ofile="]) + except GetoptError: + print("Incorrect command line arguments: generate-docs.py -o ") + exit(2) + for opt, arg in opts: + if opt == "-h": + print("Proper command line arguments: generate-docs.py -o ") + exit() + elif opt in ("-o", "--ofile"): + outputfilepath = arg -# outputfiledir = outputfilepath + outputfilename + outputfiledir = outputfilepath + outputfilename -# print("Output file is ", outputfiledir) -# return outputfiledir + print("Output file is ", outputfiledir) + return outputfiledir -OUTPUT_FILE = open('../../../docs/webhook_events.md', 'w', newline='\n', encoding='ascii') +OUTPUT_FILE = open(generate_file(), "w", newline="\n", encoding="ascii") def layer(depth: int, title: str, content: Optional[str] = None) -> None: @@ -106,6 +106,7 @@ def typed(depth: int, title: str, content: str, data_type: str) -> None: def main() -> None: + generate_file() task_config = TaskConfig( job_id=UUID(int=0), task=TaskDetails( From 778c1c4cc45328b146573b203a331bb079a38e3b Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Wed, 12 May 2021 11:14:41 -0700 Subject: [PATCH 058/110] Trying to debug. --- src/api-service/__app__/proxy/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/api-service/__app__/proxy/__init__.py b/src/api-service/__app__/proxy/__init__.py index 579647c94f..e39bbcf4a7 100644 --- a/src/api-service/__app__/proxy/__init__.py +++ b/src/api-service/__app__/proxy/__init__.py @@ -6,6 +6,7 @@ from typing import Optional import azure.functions as func +import logging from onefuzztypes.enums import ErrorCode, VmState from onefuzztypes.models import Error from onefuzztypes.requests import ProxyCreate, ProxyDelete, ProxyGet, ProxyReset @@ -89,6 +90,7 @@ def patch(req: func.HttpRequest) -> func.HttpResponse: return not_ok(request, context="ProxyReset") proxy = Proxy.get(request.region) + logging.info(proxy.region) if proxy is not None: proxy.state = VmState.stopping proxy.save() From cb2af9344e1174b83ced507d591a3808fd702fae Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Wed, 12 May 2021 11:26:56 -0700 Subject: [PATCH 059/110] Isort and black. --- src/api-service/__app__/proxy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/proxy/__init__.py b/src/api-service/__app__/proxy/__init__.py index e39bbcf4a7..cbd789bc75 100644 --- a/src/api-service/__app__/proxy/__init__.py +++ b/src/api-service/__app__/proxy/__init__.py @@ -3,10 +3,10 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +import logging from typing import Optional import azure.functions as func -import logging from onefuzztypes.enums import ErrorCode, VmState from onefuzztypes.models import Error from onefuzztypes.requests import ProxyCreate, ProxyDelete, ProxyGet, ProxyReset From c9ebf8d7dc6205ec14ba298dc3df7cdd4ebee696 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Wed, 12 May 2021 13:57:09 -0700 Subject: [PATCH 060/110] Edits to agent and ProxyForward. --- src/api-service/__app__/onefuzzlib/proxy.py | 660 +++++++++--------- .../__app__/onefuzzlib/proxy_forward.py | 7 +- src/api-service/__app__/proxy/__init__.py | 9 +- src/proxy-manager/src/config.rs | 3 + src/pytypes/extra/generate-docs.py | 3 +- src/pytypes/onefuzztypes/models.py | 1 + 6 files changed, 347 insertions(+), 336 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 5814612003..1a166b4b5a 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -1,330 +1,330 @@ -#!/usr/bin/env python -# -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -import datetime -import logging -import os -from typing import List, Optional, Tuple -from uuid import UUID, uuid4 - -from azure.mgmt.compute.models import VirtualMachine -from onefuzztypes.enums import ErrorCode, VmState -from onefuzztypes.events import EventProxyCreated, EventProxyDeleted, EventProxyFailed -from onefuzztypes.models import ( - Authentication, - Error, - Forward, - ProxyConfig, - ProxyHeartbeat, -) -from onefuzztypes.primitives import Container, Region -from pydantic import Field - -from .__version__ import __version__ -from .azure.auth import build_auth -from .azure.containers import get_file_sas_url, save_blob -from .azure.creds import get_instance_id -from .azure.ip import get_public_ip -from .azure.queue import get_queue_sas -from .azure.storage import StorageType -from .azure.vm import VM -from .events import send_event -from .extension import proxy_manager_extensions -from .orm import ORMMixin, QueryFilter -from .proxy_forward import ProxyForward - -PROXY_SKU = "Standard_B2s" -PROXY_IMAGE = "Canonical:UbuntuServer:18.04-LTS:latest" -PROXY_LOG_PREFIX = "scaleset-proxy: " - - -# This isn't intended to ever be shared to the client, hence not being in -# onefuzztypes -class Proxy(ORMMixin): - timestamp: Optional[datetime.datetime] = Field(alias="Timestamp") - created_timestamp: datetime.datetime = Field( - default_factory=datetime.datetime.utcnow - ) - proxy_id: UUID = Field(default_factory=uuid4) - region: Region - state: VmState = Field(default=VmState.init) - auth: Authentication = Field(default_factory=build_auth) - ip: Optional[str] - error: Optional[Error] - version: str = Field(default=__version__) - heartbeat: Optional[ProxyHeartbeat] - outdated: bool = Field(default=False) - - @classmethod - def key_fields(cls) -> Tuple[str, Optional[str]]: - return ("region", "proxy_id") - - def get_vm(self) -> VM: - vm = VM( - name="proxy-%s-%s" % (self.region, self.proxy_id), - region=self.region, - sku=PROXY_SKU, - image=PROXY_IMAGE, - auth=self.auth, - ) - return vm - - def init(self) -> None: - vm = self.get_vm() - vm_data = vm.get() - if vm_data: - if vm_data.provisioning_state == "Failed": - self.set_provision_failed(vm_data) - return - else: - self.save_proxy_config() - self.state = VmState.extensions_launch - else: - result = vm.create() - if isinstance(result, Error): - self.set_failed(result) - return - self.save() - - def set_provision_failed(self, vm_data: VirtualMachine) -> None: - errors = ["provisioning failed"] - for status in vm_data.instance_view.statuses: - if status.level.name.lower() == "error": - errors.append( - f"code:{status.code} status:{status.display_status} " - f"message:{status.message}" - ) - - self.set_failed( - Error( - code=ErrorCode.PROXY_FAILED, - errors=errors, - ) - ) - return - - def set_failed(self, error: Error) -> None: - if self.error is not None: - return - - logging.error(PROXY_LOG_PREFIX + "vm failed: %s - %s", self.region, error) - send_event( - EventProxyFailed(region=self.region, proxy_id=self.proxy_id, error=error) - ) - self.error = error - self.state = VmState.stopping - self.save() - - def extensions_launch(self) -> None: - vm = self.get_vm() - vm_data = vm.get() - if not vm_data: - self.set_failed( - Error( - code=ErrorCode.PROXY_FAILED, - errors=["azure not able to find vm"], - ) - ) - return - - if vm_data.provisioning_state == "Failed": - self.set_provision_failed(vm_data) - return - - ip = get_public_ip(vm_data.network_profile.network_interfaces[0].id) - if ip is None: - self.save() - return - self.ip = ip - - extensions = proxy_manager_extensions(self.region, self.proxy_id) - result = vm.add_extensions(extensions) - if isinstance(result, Error): - self.set_failed(result) - return - elif result: - self.state = VmState.running - - self.save() - - def stopping(self) -> None: - vm = self.get_vm() - if not vm.is_deleted(): - logging.info(PROXY_LOG_PREFIX + "stopping proxy: %s", self.region) - vm.delete() - self.save() - else: - self.stopped() - - def stopped(self) -> None: - logging.info(PROXY_LOG_PREFIX + "removing proxy: %s", self.region) - self.delete() - - def is_outdated(self) -> bool: - if self.version != __version__: - logging.info( - PROXY_LOG_PREFIX + "mismatch version: proxy:%s service:%s state:%s", - self.version, - __version__, - self.state, - ) - self.outdated = True - return True - if self.created_timestamp is not None: - proxy_timestamp = self.created_timestamp - if proxy_timestamp < ( - datetime.datetime.now(tz=datetime.timezone.utc) - - datetime.timedelta(minutes=30) - ): - logging.info( - PROXY_LOG_PREFIX - + "proxy older than 7 days:proxy-created:%s state:%s", - self.created_timestamp, - self.state, - ) - self.outdated = True - return True - return False - - def is_used(self) -> bool: - if len(self.get_forwards()) == 0: - logging.info(PROXY_LOG_PREFIX + "no forwards: %s", self.region) - return False - return True - - def is_alive(self) -> bool: - # Unfortunately, with and without TZ information is required for compare - # or exceptions are generated - ten_minutes_ago_no_tz = datetime.datetime.utcnow() - datetime.timedelta( - minutes=10 - ) - ten_minutes_ago = ten_minutes_ago_no_tz.astimezone(datetime.timezone.utc) - if ( - self.heartbeat is not None - and self.heartbeat.timestamp < ten_minutes_ago_no_tz - ): - logging.error( - PROXY_LOG_PREFIX + "last heartbeat is more than an 10 minutes old: " - "%s - last heartbeat:%s compared_to:%s", - self.region, - self.heartbeat, - ten_minutes_ago_no_tz, - ) - return False - - elif not self.heartbeat and self.timestamp and self.timestamp < ten_minutes_ago: - logging.error( - PROXY_LOG_PREFIX + "no heartbeat in the last 10 minutes: " - "%s timestamp: %s compared_to:%s", - self.region, - self.timestamp, - ten_minutes_ago, - ) - return False - - return True - - def get_forwards(self) -> List[Forward]: - forwards: List[Forward] = [] - for entry in ProxyForward.search_forward(region=self.region): - if entry.endtime < datetime.datetime.now(tz=datetime.timezone.utc): - entry.delete() - else: - forwards.append( - Forward( - src_port=entry.port, - dst_ip=entry.dst_ip, - dst_port=entry.dst_port, - ) - ) - return forwards - - def save_proxy_config(self) -> None: - forwards = self.get_forwards() - proxy_config = ProxyConfig( - url=get_file_sas_url( - Container("proxy-configs"), - "%s/%s/config.json" % (self.region, self.proxy_id), - StorageType.config, - read=True, - ), - notification=get_queue_sas( - "proxy", - StorageType.config, - add=True, - ), - forwards=forwards, - region=self.region, - instance_telemetry_key=os.environ.get("APPINSIGHTS_INSTRUMENTATIONKEY"), - microsoft_telemetry_key=os.environ.get("ONEFUZZ_TELEMETRY"), - instance_id=get_instance_id(), - ) - - save_blob( - Container("proxy-configs"), - "%s/%s/config.json" % (self.region, self.proxy_id), - proxy_config.json(), - StorageType.config, - ) - - @classmethod - def search_states(cls, *, states: Optional[List[VmState]] = None) -> List["Proxy"]: - query: QueryFilter = {} - if states: - query["state"] = states - return cls.search(query=query) - - @classmethod - # Question - Why does this not include is_used to check forwards? - def get_or_create(cls, region: Region) -> Optional["Proxy"]: - proxy_list = Proxy.search( - query={"region": [region], "outdated": [False]}, num_results=1 - ) - proxy_timestamp = None - if len(proxy_list) != 0: - proxy = proxy_list[0] - if proxy.version != __version__: - logging.info( - PROXY_LOG_PREFIX - + "mismatch version: proxy :%s service:%s state:%s", - proxy.version, - __version__, - proxy.state, - ) - if proxy.state != VmState.stopping: - # If the proxy is out-of-date, delete and re-create it - proxy.state = VmState.stopping - proxy.save() - proxy.outdated = True - return None - if proxy.created_timestamp is not None: - proxy_timestamp = proxy.created_timestamp - if proxy_timestamp < ( - datetime.datetime.now(tz=datetime.timezone.utc) - - datetime.timedelta(minutes=30) - ): - logging.info( - PROXY_LOG_PREFIX - + "proxy older than 7 days: proxy-created:%s state:%s", - proxy.created_timestamp, - proxy.state, - ) - if proxy.state != VmState.stopping: - # If the proxy is out-of-date, delete and re-create it - proxy.state = VmState.stopping - proxy.save() - proxy.outdated = True - return None - return proxy - - logging.info(PROXY_LOG_PREFIX + "creating proxy : region:%s", region) - proxy = Proxy(region=region) - proxy.save() - send_event(EventProxyCreated(region=region, proxy_id=proxy.proxy_id)) - return proxy - - def delete(self) -> None: - super().delete() - send_event(EventProxyDeleted(region=self.region, proxy_id=self.proxy_id)) +#!/usr/bin/env python +# +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +import datetime +import logging +import os +from typing import List, Optional, Tuple +from uuid import UUID, uuid4 + +from azure.mgmt.compute.models import VirtualMachine +from onefuzztypes.enums import ErrorCode, VmState +from onefuzztypes.events import EventProxyCreated, EventProxyDeleted, EventProxyFailed +from onefuzztypes.models import ( + Authentication, + Error, + Forward, + ProxyConfig, + ProxyHeartbeat, +) +from onefuzztypes.primitives import Container, Region +from pydantic import Field + +from .__version__ import __version__ +from .azure.auth import build_auth +from .azure.containers import get_file_sas_url, save_blob +from .azure.creds import get_instance_id +from .azure.ip import get_public_ip +from .azure.queue import get_queue_sas +from .azure.storage import StorageType +from .azure.vm import VM +from .events import send_event +from .extension import proxy_manager_extensions +from .orm import ORMMixin, QueryFilter +from .proxy_forward import ProxyForward + +PROXY_SKU = "Standard_B2s" +PROXY_IMAGE = "Canonical:UbuntuServer:18.04-LTS:latest" +PROXY_LOG_PREFIX = "scaleset-proxy: " + + +# This isn't intended to ever be shared to the client, hence not being in +# onefuzztypes +class Proxy(ORMMixin): + timestamp: Optional[datetime.datetime] = Field(alias="Timestamp") + created_timestamp: datetime.datetime = Field( + default_factory=datetime.datetime.utcnow + ) + proxy_id: UUID = Field(default_factory=uuid4) + region: Region + state: VmState = Field(default=VmState.init) + auth: Authentication = Field(default_factory=build_auth) + ip: Optional[str] + error: Optional[Error] + version: str = Field(default=__version__) + heartbeat: Optional[ProxyHeartbeat] + outdated: bool = Field(default=False) + + @classmethod + def key_fields(cls) -> Tuple[str, Optional[str]]: + return ("region", "proxy_id") + + def get_vm(self) -> VM: + vm = VM( + name="proxy-%s-%s" % (self.region, self.proxy_id), + region=self.region, + sku=PROXY_SKU, + image=PROXY_IMAGE, + auth=self.auth, + ) + return vm + + def init(self) -> None: + vm = self.get_vm() + vm_data = vm.get() + if vm_data: + if vm_data.provisioning_state == "Failed": + self.set_provision_failed(vm_data) + return + else: + self.save_proxy_config() + self.state = VmState.extensions_launch + else: + result = vm.create() + if isinstance(result, Error): + self.set_failed(result) + return + self.save() + + def set_provision_failed(self, vm_data: VirtualMachine) -> None: + errors = ["provisioning failed"] + for status in vm_data.instance_view.statuses: + if status.level.name.lower() == "error": + errors.append( + f"code:{status.code} status:{status.display_status} " + f"message:{status.message}" + ) + + self.set_failed( + Error( + code=ErrorCode.PROXY_FAILED, + errors=errors, + ) + ) + return + + def set_failed(self, error: Error) -> None: + if self.error is not None: + return + + logging.error(PROXY_LOG_PREFIX + "vm failed: %s - %s", self.region, error) + send_event( + EventProxyFailed(region=self.region, proxy_id=self.proxy_id, error=error) + ) + self.error = error + self.state = VmState.stopping + self.save() + + def extensions_launch(self) -> None: + vm = self.get_vm() + vm_data = vm.get() + if not vm_data: + self.set_failed( + Error( + code=ErrorCode.PROXY_FAILED, + errors=["azure not able to find vm"], + ) + ) + return + + if vm_data.provisioning_state == "Failed": + self.set_provision_failed(vm_data) + return + + ip = get_public_ip(vm_data.network_profile.network_interfaces[0].id) + if ip is None: + self.save() + return + self.ip = ip + + extensions = proxy_manager_extensions(self.region, self.proxy_id) + result = vm.add_extensions(extensions) + if isinstance(result, Error): + self.set_failed(result) + return + elif result: + self.state = VmState.running + + self.save() + + def stopping(self) -> None: + vm = self.get_vm() + if not vm.is_deleted(): + logging.info(PROXY_LOG_PREFIX + "stopping proxy: %s", self.region) + vm.delete() + self.save() + else: + self.stopped() + + def stopped(self) -> None: + logging.info(PROXY_LOG_PREFIX + "removing proxy: %s", self.region) + self.delete() + + def is_outdated(self) -> bool: + if self.version != __version__: + logging.info( + PROXY_LOG_PREFIX + "mismatch version: proxy:%s service:%s state:%s", + self.version, + __version__, + self.state, + ) + self.outdated = True + return True + if self.created_timestamp is not None: + proxy_timestamp = self.created_timestamp + if proxy_timestamp < ( + datetime.datetime.now(tz=datetime.timezone.utc) + - datetime.timedelta(minutes=30) + ): + logging.info( + PROXY_LOG_PREFIX + + "proxy older than 7 days:proxy-created:%s state:%s", + self.created_timestamp, + self.state, + ) + self.outdated = True + return True + return False + + def is_used(self) -> bool: + if len(self.get_forwards()) == 0: + logging.info(PROXY_LOG_PREFIX + "no forwards: %s", self.region) + return False + return True + + def is_alive(self) -> bool: + # Unfortunately, with and without TZ information is required for compare + # or exceptions are generated + ten_minutes_ago_no_tz = datetime.datetime.utcnow() - datetime.timedelta( + minutes=10 + ) + ten_minutes_ago = ten_minutes_ago_no_tz.astimezone(datetime.timezone.utc) + if ( + self.heartbeat is not None + and self.heartbeat.timestamp < ten_minutes_ago_no_tz + ): + logging.error( + PROXY_LOG_PREFIX + "last heartbeat is more than an 10 minutes old: " + "%s - last heartbeat:%s compared_to:%s", + self.region, + self.heartbeat, + ten_minutes_ago_no_tz, + ) + return False + + elif not self.heartbeat and self.timestamp and self.timestamp < ten_minutes_ago: + logging.error( + PROXY_LOG_PREFIX + "no heartbeat in the last 10 minutes: " + "%s timestamp: %s compared_to:%s", + self.region, + self.timestamp, + ten_minutes_ago, + ) + return False + + return True + + def get_forwards(self) -> List[Forward]: + forwards: List[Forward] = [] + for entry in ProxyForward.search_forward(region=self.region, proxy_id=self.proxy_id): + if entry.endtime < datetime.datetime.now(tz=datetime.timezone.utc): + entry.delete() + else: + forwards.append( + Forward( + src_port=entry.port, + dst_ip=entry.dst_ip, + dst_port=entry.dst_port, + ) + ) + return forwards + + def save_proxy_config(self) -> None: + forwards = self.get_forwards() + proxy_config = ProxyConfig( + url=get_file_sas_url( + Container("proxy-configs"), + "%s/%s/config.json" % (self.region, self.proxy_id), + StorageType.config, + read=True, + ), + notification=get_queue_sas( + "proxy", + StorageType.config, + add=True, + ), + forwards=forwards, + region=self.region, + proxy_id=self.proxy_id, + instance_telemetry_key=os.environ.get("APPINSIGHTS_INSTRUMENTATIONKEY"), + microsoft_telemetry_key=os.environ.get("ONEFUZZ_TELEMETRY"), + instance_id=get_instance_id(), + ) + + save_blob( + Container("proxy-configs"), + "%s/%s/config.json" % (self.region, self.proxy_id), + proxy_config.json(), + StorageType.config, + ) + + @classmethod + def search_states(cls, *, states: Optional[List[VmState]] = None) -> List["Proxy"]: + query: QueryFilter = {} + if states: + query["state"] = states + return cls.search(query=query) + + @classmethod + def get_or_create(cls, region: Region) -> Optional["Proxy"]: + proxy_list = Proxy.search( + query={"region": [region], "outdated": [False]}, num_results=1 + ) + proxy_timestamp = None + if len(proxy_list) != 0: + proxy = proxy_list[0] + if proxy.version != __version__: + logging.info( + PROXY_LOG_PREFIX + + "mismatch version: proxy :%s service:%s state:%s", + proxy.version, + __version__, + proxy.state, + ) + if proxy.state != VmState.stopping: + # If the proxy is out-of-date, delete and re-create it + proxy.state = VmState.stopping + proxy.save() + proxy.outdated = True + return None + if proxy.created_timestamp is not None: + proxy_timestamp = proxy.created_timestamp + if proxy_timestamp < ( + datetime.datetime.now(tz=datetime.timezone.utc) + - datetime.timedelta(minutes=30) + ): + logging.info( + PROXY_LOG_PREFIX + + "proxy older than 7 days: proxy-created:%s state:%s", + proxy.created_timestamp, + proxy.state, + ) + if proxy.state != VmState.stopping: + # If the proxy is out-of-date, delete and re-create it + proxy.state = VmState.stopping + proxy.save() + proxy.outdated = True + return None + return proxy + + logging.info(PROXY_LOG_PREFIX + "creating proxy : region:%s", region) + proxy = Proxy(region=region) + proxy.save() + send_event(EventProxyCreated(region=region, proxy_id=proxy.proxy_id)) + return proxy + + def delete(self) -> None: + super().delete() + send_event(EventProxyDeleted(region=self.region, proxy_id=self.proxy_id)) diff --git a/src/api-service/__app__/onefuzzlib/proxy_forward.py b/src/api-service/__app__/onefuzzlib/proxy_forward.py index d810e438b9..98e87fd2be 100644 --- a/src/api-service/__app__/onefuzzlib/proxy_forward.py +++ b/src/api-service/__app__/onefuzzlib/proxy_forward.py @@ -26,6 +26,7 @@ class ProxyForward(ORMMixin): port: int scaleset_id: UUID machine_id: UUID + proxy_id: UUID dst_ip: str dst_port: int endtime: datetime.datetime = Field(default_factory=datetime.datetime.utcnow) @@ -40,6 +41,7 @@ def update_or_create( region: Region, scaleset_id: UUID, machine_id: UUID, + proxy_id: UUID, dst_port: int, duration: int, ) -> Union["ProxyForward", Error]: @@ -52,6 +54,7 @@ def update_or_create( entries = cls.search_forward( scaleset_id=scaleset_id, machine_id=machine_id, + proxy_id=proxy_id, dst_port=dst_port, region=region, ) @@ -93,11 +96,12 @@ def remove_forward( cls, scaleset_id: UUID, *, + proxy_id: Optional[UUID] = None, machine_id: Optional[UUID] = None, dst_port: Optional[int] = None, ) -> List[Region]: entries = cls.search_forward( - scaleset_id=scaleset_id, machine_id=machine_id, dst_port=dst_port + scaleset_id=scaleset_id, machine_id=machine_id, proxy_id=proxy_id, dst_port=dst_port ) regions = set() for entry in entries: @@ -112,6 +116,7 @@ def search_forward( scaleset_id: Optional[UUID] = None, region: Optional[Region] = None, machine_id: Optional[UUID] = None, + proxy_id: Optional[UUID] = None, dst_port: Optional[int] = None, ) -> List["ProxyForward"]: diff --git a/src/api-service/__app__/proxy/__init__.py b/src/api-service/__app__/proxy/__init__.py index cbd789bc75..87081b02e7 100644 --- a/src/api-service/__app__/proxy/__init__.py +++ b/src/api-service/__app__/proxy/__init__.py @@ -45,6 +45,7 @@ def get(req: func.HttpRequest) -> func.HttpResponse: forwards = ProxyForward.search_forward( scaleset_id=request.scaleset_id, machine_id=request.machine_id, + proxy_id=proxy.proxy_id, dst_port=request.dst_port, ) if not forwards: @@ -68,19 +69,21 @@ def post(req: func.HttpRequest) -> func.HttpResponse: if isinstance(scaleset, Error): return not_ok(scaleset, context="debug_proxy create") + proxy = Proxy.get_or_create(scaleset.region) + if proxy: + proxy.save_proxy_config() + forward = ProxyForward.update_or_create( region=scaleset.region, scaleset_id=scaleset.scaleset_id, machine_id=request.machine_id, + proxy_id=proxy.proxy_id dst_port=request.dst_port, duration=request.duration, ) if isinstance(forward, Error): return not_ok(forward, context="debug_proxy create") - proxy = Proxy.get_or_create(scaleset.region) - if proxy: - proxy.save_proxy_config() return ok(get_result(forward, proxy)) diff --git a/src/proxy-manager/src/config.rs b/src/proxy-manager/src/config.rs index 9c54491654..92478c756f 100644 --- a/src/proxy-manager/src/config.rs +++ b/src/proxy-manager/src/config.rs @@ -48,6 +48,7 @@ pub struct ConfigData { pub instance_telemetry_key: Option, pub microsoft_telemetry_key: Option, pub region: String, + pub proxy_id: Uuid, pub url: Url, pub notification: Url, pub forwards: Vec, @@ -56,6 +57,7 @@ pub struct ConfigData { #[derive(Debug, Deserialize, Serialize, PartialEq)] pub struct NotifyResponse<'a> { pub region: &'a str, + pub proxy_id: Uuid, pub forwards: Vec, } @@ -141,6 +143,7 @@ impl Config { client .enqueue(NotifyResponse { region: &self.data.region, + proxy_id: self.data.proxy_id, forwards: self.data.forwards.clone(), }) .await?; diff --git a/src/pytypes/extra/generate-docs.py b/src/pytypes/extra/generate-docs.py index f7b7d9ec9e..221791352e 100755 --- a/src/pytypes/extra/generate-docs.py +++ b/src/pytypes/extra/generate-docs.py @@ -81,8 +81,7 @@ def generate_file() -> str: outputfilepath = arg outputfiledir = outputfilepath + outputfilename - - print("Output file is ", outputfiledir) + return outputfiledir diff --git a/src/pytypes/onefuzztypes/models.py b/src/pytypes/onefuzztypes/models.py index d3a2d1385f..ec3a1c7329 100644 --- a/src/pytypes/onefuzztypes/models.py +++ b/src/pytypes/onefuzztypes/models.py @@ -432,6 +432,7 @@ class ProxyConfig(BaseModel): url: str notification: str region: Region + proxy: UUID forwards: List[Forward] instance_telemetry_key: Optional[str] microsoft_telemetry_key: Optional[str] From e18098ee280f570df6ec6a4ea592a92c67951ccd Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Wed, 12 May 2021 13:59:43 -0700 Subject: [PATCH 061/110] Reformatting. --- src/api-service/__app__/onefuzzlib/proxy.py | 4 +++- src/api-service/__app__/onefuzzlib/proxy_forward.py | 7 +++++-- src/api-service/__app__/proxy/__init__.py | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 1a166b4b5a..bcad6ac1bc 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -228,7 +228,9 @@ def is_alive(self) -> bool: def get_forwards(self) -> List[Forward]: forwards: List[Forward] = [] - for entry in ProxyForward.search_forward(region=self.region, proxy_id=self.proxy_id): + for entry in ProxyForward.search_forward( + region=self.region, proxy_id=self.proxy_id + ): if entry.endtime < datetime.datetime.now(tz=datetime.timezone.utc): entry.delete() else: diff --git a/src/api-service/__app__/onefuzzlib/proxy_forward.py b/src/api-service/__app__/onefuzzlib/proxy_forward.py index 98e87fd2be..9ff67f2e2c 100644 --- a/src/api-service/__app__/onefuzzlib/proxy_forward.py +++ b/src/api-service/__app__/onefuzzlib/proxy_forward.py @@ -101,7 +101,10 @@ def remove_forward( dst_port: Optional[int] = None, ) -> List[Region]: entries = cls.search_forward( - scaleset_id=scaleset_id, machine_id=machine_id, proxy_id=proxy_id, dst_port=dst_port + scaleset_id=scaleset_id, + machine_id=machine_id, + proxy_id=proxy_id, + dst_port=dst_port, ) regions = set() for entry in entries: @@ -116,7 +119,7 @@ def search_forward( scaleset_id: Optional[UUID] = None, region: Optional[Region] = None, machine_id: Optional[UUID] = None, - proxy_id: Optional[UUID] = None, + proxy_id: Optional[UUID] = None, dst_port: Optional[int] = None, ) -> List["ProxyForward"]: diff --git a/src/api-service/__app__/proxy/__init__.py b/src/api-service/__app__/proxy/__init__.py index 87081b02e7..1481eb9a7b 100644 --- a/src/api-service/__app__/proxy/__init__.py +++ b/src/api-service/__app__/proxy/__init__.py @@ -77,7 +77,7 @@ def post(req: func.HttpRequest) -> func.HttpResponse: region=scaleset.region, scaleset_id=scaleset.scaleset_id, machine_id=request.machine_id, - proxy_id=proxy.proxy_id + proxy_id=proxy.proxy_id, dst_port=request.dst_port, duration=request.duration, ) From 2679ebcf6b1cc9ac7d189ef8692441a3e49cdd3d Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Wed, 12 May 2021 14:01:41 -0700 Subject: [PATCH 062/110] Reformatting generate-docs.py --- src/pytypes/extra/generate-docs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pytypes/extra/generate-docs.py b/src/pytypes/extra/generate-docs.py index 221791352e..255377a47b 100755 --- a/src/pytypes/extra/generate-docs.py +++ b/src/pytypes/extra/generate-docs.py @@ -81,7 +81,7 @@ def generate_file() -> str: outputfilepath = arg outputfiledir = outputfilepath + outputfilename - + return outputfiledir From 16a64b8a924ce278a3920ac00dd68e1de02936ec Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Wed, 12 May 2021 14:17:58 -0700 Subject: [PATCH 063/110] Fixing proxy_id references. --- src/api-service/__app__/onefuzzlib/proxy_forward.py | 1 + src/api-service/__app__/proxy/__init__.py | 4 ++-- src/pytypes/onefuzztypes/models.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy_forward.py b/src/api-service/__app__/onefuzzlib/proxy_forward.py index 9ff67f2e2c..c4076f4c62 100644 --- a/src/api-service/__app__/onefuzzlib/proxy_forward.py +++ b/src/api-service/__app__/onefuzzlib/proxy_forward.py @@ -76,6 +76,7 @@ def update_or_create( port=port, scaleset_id=scaleset_id, machine_id=machine_id, + proxy_id=proxy_id, dst_ip=private_ip, dst_port=dst_port, endtime=datetime.datetime.utcnow() + datetime.timedelta(hours=duration), diff --git a/src/api-service/__app__/proxy/__init__.py b/src/api-service/__app__/proxy/__init__.py index 1481eb9a7b..684422e2aa 100644 --- a/src/api-service/__app__/proxy/__init__.py +++ b/src/api-service/__app__/proxy/__init__.py @@ -45,7 +45,6 @@ def get(req: func.HttpRequest) -> func.HttpResponse: forwards = ProxyForward.search_forward( scaleset_id=request.scaleset_id, machine_id=request.machine_id, - proxy_id=proxy.proxy_id, dst_port=request.dst_port, ) if not forwards: @@ -70,6 +69,8 @@ def post(req: func.HttpRequest) -> func.HttpResponse: return not_ok(scaleset, context="debug_proxy create") proxy = Proxy.get_or_create(scaleset.region) + if isinstance(proxy, Error): + return not_ok(request, context="debug_proxy create") if proxy: proxy.save_proxy_config() @@ -93,7 +94,6 @@ def patch(req: func.HttpRequest) -> func.HttpResponse: return not_ok(request, context="ProxyReset") proxy = Proxy.get(request.region) - logging.info(proxy.region) if proxy is not None: proxy.state = VmState.stopping proxy.save() diff --git a/src/pytypes/onefuzztypes/models.py b/src/pytypes/onefuzztypes/models.py index ec3a1c7329..199d1d1e99 100644 --- a/src/pytypes/onefuzztypes/models.py +++ b/src/pytypes/onefuzztypes/models.py @@ -432,7 +432,7 @@ class ProxyConfig(BaseModel): url: str notification: str region: Region - proxy: UUID + proxy_id: UUID forwards: List[Forward] instance_telemetry_key: Optional[str] microsoft_telemetry_key: Optional[str] From 0dc5f78bfb39ba459255e54d1989d4cb65cef559 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Wed, 12 May 2021 14:22:34 -0700 Subject: [PATCH 064/110] Fixing imports. --- src/api-service/__app__/proxy/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/api-service/__app__/proxy/__init__.py b/src/api-service/__app__/proxy/__init__.py index 684422e2aa..b2d22732c3 100644 --- a/src/api-service/__app__/proxy/__init__.py +++ b/src/api-service/__app__/proxy/__init__.py @@ -3,7 +3,6 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. -import logging from typing import Optional import azure.functions as func From ad146c7f64372e91e8c2574d5d1308dc8b0a45cb Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Wed, 12 May 2021 14:29:43 -0700 Subject: [PATCH 065/110] Working on proxy init.py --- src/api-service/__app__/proxy/__init__.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/api-service/__app__/proxy/__init__.py b/src/api-service/__app__/proxy/__init__.py index b2d22732c3..6d15ed0c95 100644 --- a/src/api-service/__app__/proxy/__init__.py +++ b/src/api-service/__app__/proxy/__init__.py @@ -73,16 +73,16 @@ def post(req: func.HttpRequest) -> func.HttpResponse: if proxy: proxy.save_proxy_config() - forward = ProxyForward.update_or_create( - region=scaleset.region, - scaleset_id=scaleset.scaleset_id, - machine_id=request.machine_id, - proxy_id=proxy.proxy_id, - dst_port=request.dst_port, - duration=request.duration, - ) - if isinstance(forward, Error): - return not_ok(forward, context="debug_proxy create") + forward = ProxyForward.update_or_create( + region=scaleset.region, + scaleset_id=scaleset.scaleset_id, + machine_id=request.machine_id, + proxy_id=proxy.proxy_id, + dst_port=request.dst_port, + duration=request.duration, + ) + if isinstance(forward, Error): + return not_ok(forward, context="debug_proxy create") return ok(get_result(forward, proxy)) From 05c07bac6f72617c292757602cf78f712a7bfa3d Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Wed, 12 May 2021 14:32:34 -0700 Subject: [PATCH 066/110] Fixing init.py in proxy. --- src/api-service/__app__/proxy/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/api-service/__app__/proxy/__init__.py b/src/api-service/__app__/proxy/__init__.py index 6d15ed0c95..fdc46f94a5 100644 --- a/src/api-service/__app__/proxy/__init__.py +++ b/src/api-service/__app__/proxy/__init__.py @@ -68,6 +68,7 @@ def post(req: func.HttpRequest) -> func.HttpResponse: return not_ok(scaleset, context="debug_proxy create") proxy = Proxy.get_or_create(scaleset.region) + forward = None if isinstance(proxy, Error): return not_ok(request, context="debug_proxy create") if proxy: @@ -81,8 +82,8 @@ def post(req: func.HttpRequest) -> func.HttpResponse: dst_port=request.dst_port, duration=request.duration, ) - if isinstance(forward, Error): - return not_ok(forward, context="debug_proxy create") + if isinstance(forward, Error): + return not_ok(forward, context="debug_proxy create") return ok(get_result(forward, proxy)) From 2f51d3b315b2f8d804fe22d604e4aaa565d04dca Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Wed, 12 May 2021 14:46:49 -0700 Subject: [PATCH 067/110] Trying to remove proxy_id from ProxyForward. --- src/api-service/__app__/onefuzzlib/proxy.py | 3 ++- .../__app__/onefuzzlib/proxy_forward.py | 15 +++++++++------ src/api-service/__app__/proxy/__init__.py | 2 +- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index bcad6ac1bc..82dcb9024a 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -229,7 +229,8 @@ def is_alive(self) -> bool: def get_forwards(self) -> List[Forward]: forwards: List[Forward] = [] for entry in ProxyForward.search_forward( - region=self.region, proxy_id=self.proxy_id + region=self.region, + # proxy_id=self.proxy_id ): if entry.endtime < datetime.datetime.now(tz=datetime.timezone.utc): entry.delete() diff --git a/src/api-service/__app__/onefuzzlib/proxy_forward.py b/src/api-service/__app__/onefuzzlib/proxy_forward.py index c4076f4c62..91c96e32eb 100644 --- a/src/api-service/__app__/onefuzzlib/proxy_forward.py +++ b/src/api-service/__app__/onefuzzlib/proxy_forward.py @@ -41,7 +41,7 @@ def update_or_create( region: Region, scaleset_id: UUID, machine_id: UUID, - proxy_id: UUID, + # proxy_id: UUID, dst_port: int, duration: int, ) -> Union["ProxyForward", Error]: @@ -54,7 +54,7 @@ def update_or_create( entries = cls.search_forward( scaleset_id=scaleset_id, machine_id=machine_id, - proxy_id=proxy_id, + # proxy_id=proxy_id, dst_port=dst_port, region=region, ) @@ -76,7 +76,7 @@ def update_or_create( port=port, scaleset_id=scaleset_id, machine_id=machine_id, - proxy_id=proxy_id, + # proxy_id=proxy_id, dst_ip=private_ip, dst_port=dst_port, endtime=datetime.datetime.utcnow() + datetime.timedelta(hours=duration), @@ -97,14 +97,14 @@ def remove_forward( cls, scaleset_id: UUID, *, - proxy_id: Optional[UUID] = None, + # proxy_id: Optional[UUID] = None, machine_id: Optional[UUID] = None, dst_port: Optional[int] = None, ) -> List[Region]: entries = cls.search_forward( scaleset_id=scaleset_id, machine_id=machine_id, - proxy_id=proxy_id, + # proxy_id=proxy_id, dst_port=dst_port, ) regions = set() @@ -120,7 +120,7 @@ def search_forward( scaleset_id: Optional[UUID] = None, region: Optional[Region] = None, machine_id: Optional[UUID] = None, - proxy_id: Optional[UUID] = None, + # proxy_id: Optional[UUID] = None, dst_port: Optional[int] = None, ) -> List["ProxyForward"]: @@ -134,6 +134,9 @@ def search_forward( if machine_id is not None: query["machine_id"] = [machine_id] + # if proxy_id is not None: + # query["proxy_id"] = [proxy_id] + if dst_port is not None: query["dst_port"] = [dst_port] diff --git a/src/api-service/__app__/proxy/__init__.py b/src/api-service/__app__/proxy/__init__.py index fdc46f94a5..0396b2f8ea 100644 --- a/src/api-service/__app__/proxy/__init__.py +++ b/src/api-service/__app__/proxy/__init__.py @@ -78,7 +78,7 @@ def post(req: func.HttpRequest) -> func.HttpResponse: region=scaleset.region, scaleset_id=scaleset.scaleset_id, machine_id=request.machine_id, - proxy_id=proxy.proxy_id, + # proxy_id=proxy.proxy_id, dst_port=request.dst_port, duration=request.duration, ) From 42f17a39eda50680d37fa10775f2e08a0e2a5b32 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Wed, 12 May 2021 14:55:22 -0700 Subject: [PATCH 068/110] Removing proxy_id reference from ProxyForward. --- src/api-service/__app__/proxy/__init__.py | 26 ++++++++++------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/api-service/__app__/proxy/__init__.py b/src/api-service/__app__/proxy/__init__.py index 0396b2f8ea..ee8b02a450 100644 --- a/src/api-service/__app__/proxy/__init__.py +++ b/src/api-service/__app__/proxy/__init__.py @@ -67,24 +67,20 @@ def post(req: func.HttpRequest) -> func.HttpResponse: if isinstance(scaleset, Error): return not_ok(scaleset, context="debug_proxy create") - proxy = Proxy.get_or_create(scaleset.region) - forward = None - if isinstance(proxy, Error): - return not_ok(request, context="debug_proxy create") - if proxy: - proxy.save_proxy_config() - - forward = ProxyForward.update_or_create( - region=scaleset.region, - scaleset_id=scaleset.scaleset_id, - machine_id=request.machine_id, - # proxy_id=proxy.proxy_id, - dst_port=request.dst_port, - duration=request.duration, - ) + forward = ProxyForward.update_or_create( + region=scaleset.region, + scaleset_id=scaleset.scaleset_id, + machine_id=request.machine_id, + # proxy_id=proxy.proxy_id, + dst_port=request.dst_port, + duration=request.duration, + ) if isinstance(forward, Error): return not_ok(forward, context="debug_proxy create") + proxy = Proxy.get_or_create(scaleset.region) + if proxy: + proxy.save_proxy_config() return ok(get_result(forward, proxy)) From 17a6feb2b995c52c94528cd19a0a0fe81aa563a2 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Wed, 12 May 2021 15:00:27 -0700 Subject: [PATCH 069/110] Removing proxy_id reference. --- src/api-service/__app__/onefuzzlib/proxy_forward.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy_forward.py b/src/api-service/__app__/onefuzzlib/proxy_forward.py index 91c96e32eb..5677156d46 100644 --- a/src/api-service/__app__/onefuzzlib/proxy_forward.py +++ b/src/api-service/__app__/onefuzzlib/proxy_forward.py @@ -26,7 +26,7 @@ class ProxyForward(ORMMixin): port: int scaleset_id: UUID machine_id: UUID - proxy_id: UUID + # proxy_id: UUID dst_ip: str dst_port: int endtime: datetime.datetime = Field(default_factory=datetime.datetime.utcnow) From 794d682c6140eb0ff6a703b397d9fdf0d2744332 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Wed, 12 May 2021 15:10:01 -0700 Subject: [PATCH 070/110] Adding proxy_id to heartbeat. --- src/api-service/__app__/queue_proxy_update/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/queue_proxy_update/__init__.py b/src/api-service/__app__/queue_proxy_update/__init__.py index 93a141cf19..52390c2111 100644 --- a/src/api-service/__app__/queue_proxy_update/__init__.py +++ b/src/api-service/__app__/queue_proxy_update/__init__.py @@ -18,7 +18,7 @@ def main(msg: func.QueueMessage, dashboard: func.Out[str]) -> None: logging.info(PROXY_LOG_PREFIX + "heartbeat: %s", body) raw = json.loads(body) heartbeat = ProxyHeartbeat.parse_obj(raw) - proxy = Proxy.get(heartbeat.region) + proxy = Proxy.get(heartbeat.region, heartbeat.proxy_id) if proxy is None: logging.warning( PROXY_LOG_PREFIX + "received heartbeat for missing proxy: %s", body From b11fc96fe9769a37bf3eb0b12682a768f6abb05d Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Wed, 12 May 2021 15:11:28 -0700 Subject: [PATCH 071/110] Adding proxy_id field to heartbeat. --- src/pytypes/onefuzztypes/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pytypes/onefuzztypes/models.py b/src/pytypes/onefuzztypes/models.py index 199d1d1e99..77d29f3200 100644 --- a/src/pytypes/onefuzztypes/models.py +++ b/src/pytypes/onefuzztypes/models.py @@ -441,6 +441,7 @@ class ProxyConfig(BaseModel): class ProxyHeartbeat(BaseModel): region: Region + proxy_id: UUID forwards: List[Forward] timestamp: datetime = Field(default_factory=datetime.utcnow) From 7e1056a6539fac7a33a9249ecd4826ac7c85e70a Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Wed, 12 May 2021 19:04:18 -0700 Subject: [PATCH 072/110] Adding is_used() chat to get_or_create before stopping VM. --- src/api-service/__app__/onefuzzlib/proxy.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 82dcb9024a..a9cfec92d1 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -229,7 +229,7 @@ def is_alive(self) -> bool: def get_forwards(self) -> List[Forward]: forwards: List[Forward] = [] for entry in ProxyForward.search_forward( - region=self.region, + region=self.region, # proxy_id=self.proxy_id ): if entry.endtime < datetime.datetime.now(tz=datetime.timezone.utc): @@ -296,7 +296,7 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: __version__, proxy.state, ) - if proxy.state != VmState.stopping: + if proxy.state != VmState.stopping and not proxy.is_used(): # If the proxy is out-of-date, delete and re-create it proxy.state = VmState.stopping proxy.save() @@ -314,7 +314,7 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: proxy.created_timestamp, proxy.state, ) - if proxy.state != VmState.stopping: + if proxy.state != VmState.stopping and not proxy.is_used(): # If the proxy is out-of-date, delete and re-create it proxy.state = VmState.stopping proxy.save() From 9c609eed63be30f462c1c7e459e5697da61134fd Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Wed, 12 May 2021 19:06:30 -0700 Subject: [PATCH 073/110] black reformatting. --- src/api-service/__app__/onefuzzlib/proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index a9cfec92d1..22045787c3 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -229,7 +229,7 @@ def is_alive(self) -> bool: def get_forwards(self) -> List[Forward]: forwards: List[Forward] = [] for entry in ProxyForward.search_forward( - region=self.region, + region=self.region, # proxy_id=self.proxy_id ): if entry.endtime < datetime.datetime.now(tz=datetime.timezone.utc): From 804bf1b652d72532b2a28b2d749d74065b4837bb Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Thu, 13 May 2021 10:38:48 -0700 Subject: [PATCH 074/110] Fixing proxy.save calls. --- src/api-service/__app__/onefuzzlib/proxy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 22045787c3..349de08b79 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -299,8 +299,8 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: if proxy.state != VmState.stopping and not proxy.is_used(): # If the proxy is out-of-date, delete and re-create it proxy.state = VmState.stopping - proxy.save() proxy.outdated = True + proxy.save() return None if proxy.created_timestamp is not None: proxy_timestamp = proxy.created_timestamp @@ -317,8 +317,8 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: if proxy.state != VmState.stopping and not proxy.is_used(): # If the proxy is out-of-date, delete and re-create it proxy.state = VmState.stopping - proxy.save() proxy.outdated = True + proxy.save() return None return proxy From 17b2f6aab43970a03288d52e468954c63b0f6587 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Thu, 13 May 2021 10:55:47 -0700 Subject: [PATCH 075/110] Rerunning due to agent error. --- src/api-service/__app__/onefuzzlib/proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 349de08b79..3f4d142593 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -297,7 +297,7 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: proxy.state, ) if proxy.state != VmState.stopping and not proxy.is_used(): - # If the proxy is out-of-date, delete and re-create it + # If the proxy is out-of-date, delete and recreate it proxy.state = VmState.stopping proxy.outdated = True proxy.save() From 9ba96c6d4f830da1a411e85f1eb9efb258ccfbde Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Thu, 13 May 2021 11:31:34 -0700 Subject: [PATCH 076/110] New file for running lint tools against api-service. --- src/api-service/__app__/onefuzzlib/proxy.py | 2 +- src/ci/api-service-toolcheck.sh | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 src/ci/api-service-toolcheck.sh diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 3f4d142593..349de08b79 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -297,7 +297,7 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: proxy.state, ) if proxy.state != VmState.stopping and not proxy.is_used(): - # If the proxy is out-of-date, delete and recreate it + # If the proxy is out-of-date, delete and re-create it proxy.state = VmState.stopping proxy.outdated = True proxy.save() diff --git a/src/ci/api-service-toolcheck.sh b/src/ci/api-service-toolcheck.sh new file mode 100644 index 0000000000..eeed76630f --- /dev/null +++ b/src/ci/api-service-toolcheck.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +set -ex + +cd src/api-service + +pip install -r requirements-dev.txt +# pip install -r requirements-lint.txt + +black ./__app__ --check +flake8 ./__app__ +bandit -r ./__app__ +isort --profile black ./__app__ --check +mypy ./__app__ --ignore-missing-imports +pytest -v tests \ No newline at end of file From 22d9b3b55b2741806105fe89f54af9729f0c7e52 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Thu, 13 May 2021 11:52:35 -0700 Subject: [PATCH 077/110] Rerunning. --- src/pytypes/extra/generate-docs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pytypes/extra/generate-docs.py b/src/pytypes/extra/generate-docs.py index 255377a47b..2598b53df9 100755 --- a/src/pytypes/extra/generate-docs.py +++ b/src/pytypes/extra/generate-docs.py @@ -71,11 +71,11 @@ def generate_file() -> str: try: opts, args = getopt(argv[1:], "hi:o:", ["ifile=", "ofile="]) except GetoptError: - print("Incorrect command line arguments: generate-docs.py -o ") + print("Incorrect command line argument: generate-docs.py -o ") exit(2) for opt, arg in opts: if opt == "-h": - print("Proper command line arguments: generate-docs.py -o ") + print("Proper command line argument: generate-docs.py -o ") exit() elif opt in ("-o", "--ofile"): outputfilepath = arg From 0b1738a2a1a94ca2e2b728c8a042e2976a7849a9 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Thu, 13 May 2021 16:08:06 -0700 Subject: [PATCH 078/110] Fixing timer_daily to check if existing proxies for region. Working to add proxy_id to forwards. --- src/api-service/__app__/onefuzzlib/proxy.py | 3 +-- .../__app__/onefuzzlib/proxy_forward.py | 15 ++++++--------- src/api-service/__app__/proxy/__init__.py | 2 +- src/api-service/__app__/timer_daily/__init__.py | 5 ++++- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 349de08b79..aa5ee175c5 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -229,8 +229,7 @@ def is_alive(self) -> bool: def get_forwards(self) -> List[Forward]: forwards: List[Forward] = [] for entry in ProxyForward.search_forward( - region=self.region, - # proxy_id=self.proxy_id + region=self.region, proxy_id=self.proxy_id ): if entry.endtime < datetime.datetime.now(tz=datetime.timezone.utc): entry.delete() diff --git a/src/api-service/__app__/onefuzzlib/proxy_forward.py b/src/api-service/__app__/onefuzzlib/proxy_forward.py index 5677156d46..1ba8c6083f 100644 --- a/src/api-service/__app__/onefuzzlib/proxy_forward.py +++ b/src/api-service/__app__/onefuzzlib/proxy_forward.py @@ -26,7 +26,7 @@ class ProxyForward(ORMMixin): port: int scaleset_id: UUID machine_id: UUID - # proxy_id: UUID + proxy_id: UUID dst_ip: str dst_port: int endtime: datetime.datetime = Field(default_factory=datetime.datetime.utcnow) @@ -41,7 +41,6 @@ def update_or_create( region: Region, scaleset_id: UUID, machine_id: UUID, - # proxy_id: UUID, dst_port: int, duration: int, ) -> Union["ProxyForward", Error]: @@ -54,7 +53,6 @@ def update_or_create( entries = cls.search_forward( scaleset_id=scaleset_id, machine_id=machine_id, - # proxy_id=proxy_id, dst_port=dst_port, region=region, ) @@ -76,7 +74,6 @@ def update_or_create( port=port, scaleset_id=scaleset_id, machine_id=machine_id, - # proxy_id=proxy_id, dst_ip=private_ip, dst_port=dst_port, endtime=datetime.datetime.utcnow() + datetime.timedelta(hours=duration), @@ -97,14 +94,14 @@ def remove_forward( cls, scaleset_id: UUID, *, - # proxy_id: Optional[UUID] = None, + proxy_id: Optional[UUID] = None, machine_id: Optional[UUID] = None, dst_port: Optional[int] = None, ) -> List[Region]: entries = cls.search_forward( scaleset_id=scaleset_id, machine_id=machine_id, - # proxy_id=proxy_id, + proxy_id=proxy_id, dst_port=dst_port, ) regions = set() @@ -120,7 +117,7 @@ def search_forward( scaleset_id: Optional[UUID] = None, region: Optional[Region] = None, machine_id: Optional[UUID] = None, - # proxy_id: Optional[UUID] = None, + proxy_id: Optional[UUID] = None, dst_port: Optional[int] = None, ) -> List["ProxyForward"]: @@ -134,8 +131,8 @@ def search_forward( if machine_id is not None: query["machine_id"] = [machine_id] - # if proxy_id is not None: - # query["proxy_id"] = [proxy_id] + if proxy_id is not None: + query["proxy_id"] = [proxy_id] if dst_port is not None: query["dst_port"] = [dst_port] diff --git a/src/api-service/__app__/proxy/__init__.py b/src/api-service/__app__/proxy/__init__.py index ee8b02a450..26fc5784bc 100644 --- a/src/api-service/__app__/proxy/__init__.py +++ b/src/api-service/__app__/proxy/__init__.py @@ -71,7 +71,6 @@ def post(req: func.HttpRequest) -> func.HttpResponse: region=scaleset.region, scaleset_id=scaleset.scaleset_id, machine_id=request.machine_id, - # proxy_id=proxy.proxy_id, dst_port=request.dst_port, duration=request.duration, ) @@ -81,6 +80,7 @@ def post(req: func.HttpRequest) -> func.HttpResponse: proxy = Proxy.get_or_create(scaleset.region) if proxy: proxy.save_proxy_config() + forward.proxy_id = proxy.proxy_id return ok(get_result(forward, proxy)) diff --git a/src/api-service/__app__/timer_daily/__init__.py b/src/api-service/__app__/timer_daily/__init__.py index a3bc3ae488..48de63f1c3 100644 --- a/src/api-service/__app__/timer_daily/__init__.py +++ b/src/api-service/__app__/timer_daily/__init__.py @@ -17,7 +17,10 @@ def main(mytimer: func.TimerRequest, dashboard: func.Out[str]) -> None: # noqa: F841 for proxy in Proxy.search(): - if proxy.is_outdated(): + if ( + proxy.is_outdated() + and len(Proxy.search(query={"region": [region], "outdated": [False]})) == 0 + ): logging.info("outdated proxy, creating new one.") new_proxy = Proxy(region=proxy.region) new_proxy.save() From e5a173478ffb305ac848a0bad04200649d873a13 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Fri, 14 May 2021 09:00:43 -0700 Subject: [PATCH 079/110] Fixing region reference. --- src/api-service/__app__/timer_daily/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/api-service/__app__/timer_daily/__init__.py b/src/api-service/__app__/timer_daily/__init__.py index 48de63f1c3..b43a2660cf 100644 --- a/src/api-service/__app__/timer_daily/__init__.py +++ b/src/api-service/__app__/timer_daily/__init__.py @@ -19,7 +19,8 @@ def main(mytimer: func.TimerRequest, dashboard: func.Out[str]) -> None: # noqa: for proxy in Proxy.search(): if ( proxy.is_outdated() - and len(Proxy.search(query={"region": [region], "outdated": [False]})) == 0 + and len(Proxy.search(query={"region": [proxy.region], "outdated": [False]})) + == 0 ): logging.info("outdated proxy, creating new one.") new_proxy = Proxy(region=proxy.region) From 9a9415da247a3428b80c7abe69b90ed18dfae120 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Fri, 14 May 2021 10:02:51 -0700 Subject: [PATCH 080/110] Trying to trigger merge. --- src/ci/onefuzztypes.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/onefuzztypes.sh b/src/ci/onefuzztypes.sh index 1d085e9ec6..5380960193 100755 --- a/src/ci/onefuzztypes.sh +++ b/src/ci/onefuzztypes.sh @@ -21,7 +21,7 @@ pytest -v tests cp dist/*.* ../../artifacts/sdk -echo 'verify webhook docs are up-to-date' +echo 'verify webhook docs are up-to-date' python -m venv build-docs . build-docs/bin/activate pip install -e . From da2069cf08de9b9a09d71398fac747e905eef52d Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Fri, 14 May 2021 10:13:23 -0700 Subject: [PATCH 081/110] Merging changes to onefuzztypes and generate-docs with main. --- src/ci/onefuzztypes.sh | 2 +- src/pytypes/extra/generate-docs.py | 63 ++++++++++-------------------- 2 files changed, 21 insertions(+), 44 deletions(-) diff --git a/src/ci/onefuzztypes.sh b/src/ci/onefuzztypes.sh index 5380960193..3b28be35df 100755 --- a/src/ci/onefuzztypes.sh +++ b/src/ci/onefuzztypes.sh @@ -25,7 +25,7 @@ echo 'verify webhook docs are up-to-date' python -m venv build-docs . build-docs/bin/activate pip install -e . -python extra/generate-docs.py -o "../../docs/" +python extra/generate-docs.py ../../docs/webhook_events.md git diff ../../docs/webhook_events.md deactivate rm -rf build-docs diff --git a/src/pytypes/extra/generate-docs.py b/src/pytypes/extra/generate-docs.py index 2598b53df9..a84d694ddd 100755 --- a/src/pytypes/extra/generate-docs.py +++ b/src/pytypes/extra/generate-docs.py @@ -3,8 +3,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -from getopt import GetoptError, getopt -from sys import argv, exit +from sys from typing import List, Optional from uuid import UUID @@ -65,43 +64,19 @@ ZERO_SHA256 = "0" * len(EMPTY_SHA256) -def generate_file() -> str: - outputfilename = "webhook_events.md" - outputfilepath = "./" - try: - opts, args = getopt(argv[1:], "hi:o:", ["ifile=", "ofile="]) - except GetoptError: - print("Incorrect command line argument: generate-docs.py -o ") - exit(2) - for opt, arg in opts: - if opt == "-h": - print("Proper command line argument: generate-docs.py -o ") - exit() - elif opt in ("-o", "--ofile"): - outputfilepath = arg - - outputfiledir = outputfilepath + outputfilename - - return outputfiledir - - -OUTPUT_FILE = open(generate_file(), "w", newline="\n", encoding="ascii") - - def layer(depth: int, title: str, content: Optional[str] = None) -> None: print(f"{'#' * depth} {title}\n") - OUTPUT_FILE.write(f"{'#' * depth} {title}\n") - OUTPUT_FILE.write("\n") + result = f"{'#' * depth} {title}\n" if content is not None: print(f"{content}\n") - OUTPUT_FILE.write(f"{content}\n") - OUTPUT_FILE.write("\n") + result = f"{content}\n" + def typed(depth: int, title: str, content: str, data_type: str) -> None: print(f"{'#' * depth} {title}\n\n```{data_type}\n{content}\n```\n") - OUTPUT_FILE.write(f"{'#' * depth} {title}\n\n```{data_type}\n{content}\n```\n") - OUTPUT_FILE.write("\n") + result = f"{'#' * depth} {title}\n\n```{data_type}\n{content}\n```\n" + def main() -> None: @@ -290,49 +265,51 @@ def main() -> None: instance_name="example", ) - layer( + result = "" + result += layer( 1, "Webhook Events", "This document describes the basic webhook event subscriptions " "available in OneFuzz", ) - layer( + result += layer( 2, "Payload", "Each event will be submitted via HTTP POST to the user provided URL.", ) - typed( + result += typed( 3, "Example", message.json(indent=4, exclude_none=True, sort_keys=True), "json", ) - layer(2, "Event Types (EventType)") + result += layer(2, "Event Types (EventType)") event_map = {get_event_type(x).name: x for x in examples} for name in sorted(event_map.keys()): print(f"* [{name}](#{name})") - OUTPUT_FILE.write(f"* [{name}](#{name})") - OUTPUT_FILE.write("\n") + result = f"* [{name}](#{name})" + print() - OUTPUT_FILE.write("\n") + for name in sorted(event_map.keys()): example = event_map[name] - layer(3, name) - typed( + result += layer(3, name) + result += typed( 4, "Example", example.json(indent=4, exclude_none=True, sort_keys=True), "json", ) - typed(4, "Schema", example.schema_json(indent=4, sort_keys=True), "json") + result += typed(4, "Schema", example.schema_json(indent=4, sort_keys=True), "json") - typed(2, "Full Event Schema", message.schema_json(indent=4, sort_keys=True), "json") - OUTPUT_FILE.close() + result += typed(2, "Full Event Schema", message.schema_json(indent=4, sort_keys=True), "json") + with open(filename, "w", newline="\n", encoding="utf8") as handle: + handle.write(result) if __name__ == "__main__": From 1e4e4a09808927ce5e1dfe38bcf207104ef0d98c Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Fri, 14 May 2021 10:50:05 -0700 Subject: [PATCH 082/110] Reformatting generate_docs. --- src/pytypes/extra/generate-docs.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pytypes/extra/generate-docs.py b/src/pytypes/extra/generate-docs.py index 892243fade..5157340e00 100755 --- a/src/pytypes/extra/generate-docs.py +++ b/src/pytypes/extra/generate-docs.py @@ -300,7 +300,6 @@ def main() -> None: result += "\n" - for name in sorted(event_map.keys()): example = event_map[name] result += layer(3, name) From bfee9b2e83c43cfd1068329c0a8c0ebebac53a80 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Fri, 14 May 2021 10:52:36 -0700 Subject: [PATCH 083/110] Making proxy_id optional. --- src/api-service/__app__/onefuzzlib/proxy_forward.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy_forward.py b/src/api-service/__app__/onefuzzlib/proxy_forward.py index 1ba8c6083f..b98cd38e4e 100644 --- a/src/api-service/__app__/onefuzzlib/proxy_forward.py +++ b/src/api-service/__app__/onefuzzlib/proxy_forward.py @@ -26,7 +26,7 @@ class ProxyForward(ORMMixin): port: int scaleset_id: UUID machine_id: UUID - proxy_id: UUID + proxy_id: Optional[UUID] dst_ip: str dst_port: int endtime: datetime.datetime = Field(default_factory=datetime.datetime.utcnow) From 9bbf3df7715c78e7403849e69d6a4a7f4c4e6dc3 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Fri, 14 May 2021 15:56:27 -0700 Subject: [PATCH 084/110] Adding save for Proxy Forwards. --- src/api-service/__app__/proxy/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/api-service/__app__/proxy/__init__.py b/src/api-service/__app__/proxy/__init__.py index 26fc5784bc..5734d66f33 100644 --- a/src/api-service/__app__/proxy/__init__.py +++ b/src/api-service/__app__/proxy/__init__.py @@ -79,8 +79,9 @@ def post(req: func.HttpRequest) -> func.HttpResponse: proxy = Proxy.get_or_create(scaleset.region) if proxy: - proxy.save_proxy_config() forward.proxy_id = proxy.proxy_id + forward.save() + proxy.save_proxy_config() return ok(get_result(forward, proxy)) From 28a8ab1c0ccd2c440e94ca1ae7f398b93701e8f6 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Mon, 17 May 2021 09:57:31 -0700 Subject: [PATCH 085/110] Fixing call to is_used --- src/api-service/__app__/timer_daily/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/timer_daily/__init__.py b/src/api-service/__app__/timer_daily/__init__.py index b43a2660cf..99c08a2b1e 100644 --- a/src/api-service/__app__/timer_daily/__init__.py +++ b/src/api-service/__app__/timer_daily/__init__.py @@ -26,7 +26,7 @@ def main(mytimer: func.TimerRequest, dashboard: func.Out[str]) -> None: # noqa: new_proxy = Proxy(region=proxy.region) new_proxy.save() send_event(EventProxyCreated(region=proxy.region, proxy_id=proxy.proxy_id)) - if not proxy.is_used: + if not proxy.is_used(): logging.info("stopping proxy") proxy.state = VmState.stopping proxy.save() From 7e3797234158cc75202b5a6c0731183322cdd4fb Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Mon, 17 May 2021 13:48:10 -0700 Subject: [PATCH 086/110] Updating timer_daily with separate conditions. --- .../__app__/onefuzzlib/tasks/main.py | 1 - .../__app__/timer_daily/__init__.py | 23 +++++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/tasks/main.py b/src/api-service/__app__/onefuzzlib/tasks/main.py index dfcd2bd3a5..caf14e3704 100644 --- a/src/api-service/__app__/onefuzzlib/tasks/main.py +++ b/src/api-service/__app__/onefuzzlib/tasks/main.py @@ -128,7 +128,6 @@ def stopping(self) -> None: from ..jobs import Job logging.info("stopping task: %s:%s", self.job_id, self.task_id) - ProxyForward.remove_forward(self.task_id) delete_queue(str(self.task_id), StorageType.corpus) Node.stop_task(self.task_id) self.set_state(TaskState.stopped) diff --git a/src/api-service/__app__/timer_daily/__init__.py b/src/api-service/__app__/timer_daily/__init__.py index 99c08a2b1e..0054e00701 100644 --- a/src/api-service/__app__/timer_daily/__init__.py +++ b/src/api-service/__app__/timer_daily/__init__.py @@ -17,15 +17,20 @@ def main(mytimer: func.TimerRequest, dashboard: func.Out[str]) -> None: # noqa: F841 for proxy in Proxy.search(): - if ( - proxy.is_outdated() - and len(Proxy.search(query={"region": [proxy.region], "outdated": [False]})) - == 0 - ): - logging.info("outdated proxy, creating new one.") - new_proxy = Proxy(region=proxy.region) - new_proxy.save() - send_event(EventProxyCreated(region=proxy.region, proxy_id=proxy.proxy_id)) + if proxy.is_outdated(): + logging.info("marking proxy as outdated.") + proxy.outdated = True + proxy.save() + if ( + len(Proxy.search(query={"region": [proxy.region], "outdated": [False]})) + == 0 + ): + logging.info("outdated proxy, creating new one.") + new_proxy = Proxy(region=proxy.region) + new_proxy.save() + send_event( + EventProxyCreated(region=proxy.region, proxy_id=proxy.proxy_id) + ) if not proxy.is_used(): logging.info("stopping proxy") proxy.state = VmState.stopping From bab9d3e11fbe6c228292a0ec29aae2cdd728a62c Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Mon, 17 May 2021 14:56:14 -0700 Subject: [PATCH 087/110] Removing import. --- src/api-service/__app__/onefuzzlib/tasks/main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/tasks/main.py b/src/api-service/__app__/onefuzzlib/tasks/main.py index b9191a9b80..b78d6b4364 100644 --- a/src/api-service/__app__/onefuzzlib/tasks/main.py +++ b/src/api-service/__app__/onefuzzlib/tasks/main.py @@ -25,7 +25,6 @@ from ..azure.storage import StorageType from ..events import send_event from ..orm import MappingIntStrAny, ORMMixin, QueryFilter -from ..proxy_forward import ProxyForward from ..workers.nodes import Node, NodeTasks from ..workers.pools import Pool from ..workers.scalesets import Scaleset From 84281da18adc096434354c028f395ed21b0519b7 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Mon, 17 May 2021 15:13:08 -0700 Subject: [PATCH 088/110] Changing minutes to 5 minutes for testing. --- src/api-service/__app__/onefuzzlib/proxy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index aa5ee175c5..036f631de4 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -176,7 +176,7 @@ def is_outdated(self) -> bool: proxy_timestamp = self.created_timestamp if proxy_timestamp < ( datetime.datetime.now(tz=datetime.timezone.utc) - - datetime.timedelta(minutes=30) + - datetime.timedelta(minutes=5) ): logging.info( PROXY_LOG_PREFIX @@ -305,7 +305,7 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: proxy_timestamp = proxy.created_timestamp if proxy_timestamp < ( datetime.datetime.now(tz=datetime.timezone.utc) - - datetime.timedelta(minutes=30) + - datetime.timedelta(minutes=5) ): logging.info( PROXY_LOG_PREFIX From 1feebb52ac66f8e69e98971523b3246961adaf70 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Mon, 17 May 2021 16:52:42 -0700 Subject: [PATCH 089/110] Final Draft: Changing Proxy lifetime back to 7 days. --- src/api-service/__app__/onefuzzlib/proxy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 036f631de4..db95814448 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -176,7 +176,7 @@ def is_outdated(self) -> bool: proxy_timestamp = self.created_timestamp if proxy_timestamp < ( datetime.datetime.now(tz=datetime.timezone.utc) - - datetime.timedelta(minutes=5) + - datetime.timedelta(days=7) ): logging.info( PROXY_LOG_PREFIX @@ -305,7 +305,7 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: proxy_timestamp = proxy.created_timestamp if proxy_timestamp < ( datetime.datetime.now(tz=datetime.timezone.utc) - - datetime.timedelta(minutes=5) + - datetime.timedelta(days=7) ): logging.info( PROXY_LOG_PREFIX From ac9bf25ad2b70e1f9ced850ef12af04b13351665 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 18 May 2021 09:24:17 -0700 Subject: [PATCH 090/110] Fixing line endings. --- src/api-service/__app__/onefuzzlib/proxy.py | 664 ++++++++++---------- 1 file changed, 332 insertions(+), 332 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index db95814448..cf4b651628 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -1,332 +1,332 @@ -#!/usr/bin/env python -# -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -import datetime -import logging -import os -from typing import List, Optional, Tuple -from uuid import UUID, uuid4 - -from azure.mgmt.compute.models import VirtualMachine -from onefuzztypes.enums import ErrorCode, VmState -from onefuzztypes.events import EventProxyCreated, EventProxyDeleted, EventProxyFailed -from onefuzztypes.models import ( - Authentication, - Error, - Forward, - ProxyConfig, - ProxyHeartbeat, -) -from onefuzztypes.primitives import Container, Region -from pydantic import Field - -from .__version__ import __version__ -from .azure.auth import build_auth -from .azure.containers import get_file_sas_url, save_blob -from .azure.creds import get_instance_id -from .azure.ip import get_public_ip -from .azure.queue import get_queue_sas -from .azure.storage import StorageType -from .azure.vm import VM -from .events import send_event -from .extension import proxy_manager_extensions -from .orm import ORMMixin, QueryFilter -from .proxy_forward import ProxyForward - -PROXY_SKU = "Standard_B2s" -PROXY_IMAGE = "Canonical:UbuntuServer:18.04-LTS:latest" -PROXY_LOG_PREFIX = "scaleset-proxy: " - - -# This isn't intended to ever be shared to the client, hence not being in -# onefuzztypes -class Proxy(ORMMixin): - timestamp: Optional[datetime.datetime] = Field(alias="Timestamp") - created_timestamp: datetime.datetime = Field( - default_factory=datetime.datetime.utcnow - ) - proxy_id: UUID = Field(default_factory=uuid4) - region: Region - state: VmState = Field(default=VmState.init) - auth: Authentication = Field(default_factory=build_auth) - ip: Optional[str] - error: Optional[Error] - version: str = Field(default=__version__) - heartbeat: Optional[ProxyHeartbeat] - outdated: bool = Field(default=False) - - @classmethod - def key_fields(cls) -> Tuple[str, Optional[str]]: - return ("region", "proxy_id") - - def get_vm(self) -> VM: - vm = VM( - name="proxy-%s-%s" % (self.region, self.proxy_id), - region=self.region, - sku=PROXY_SKU, - image=PROXY_IMAGE, - auth=self.auth, - ) - return vm - - def init(self) -> None: - vm = self.get_vm() - vm_data = vm.get() - if vm_data: - if vm_data.provisioning_state == "Failed": - self.set_provision_failed(vm_data) - return - else: - self.save_proxy_config() - self.state = VmState.extensions_launch - else: - result = vm.create() - if isinstance(result, Error): - self.set_failed(result) - return - self.save() - - def set_provision_failed(self, vm_data: VirtualMachine) -> None: - errors = ["provisioning failed"] - for status in vm_data.instance_view.statuses: - if status.level.name.lower() == "error": - errors.append( - f"code:{status.code} status:{status.display_status} " - f"message:{status.message}" - ) - - self.set_failed( - Error( - code=ErrorCode.PROXY_FAILED, - errors=errors, - ) - ) - return - - def set_failed(self, error: Error) -> None: - if self.error is not None: - return - - logging.error(PROXY_LOG_PREFIX + "vm failed: %s - %s", self.region, error) - send_event( - EventProxyFailed(region=self.region, proxy_id=self.proxy_id, error=error) - ) - self.error = error - self.state = VmState.stopping - self.save() - - def extensions_launch(self) -> None: - vm = self.get_vm() - vm_data = vm.get() - if not vm_data: - self.set_failed( - Error( - code=ErrorCode.PROXY_FAILED, - errors=["azure not able to find vm"], - ) - ) - return - - if vm_data.provisioning_state == "Failed": - self.set_provision_failed(vm_data) - return - - ip = get_public_ip(vm_data.network_profile.network_interfaces[0].id) - if ip is None: - self.save() - return - self.ip = ip - - extensions = proxy_manager_extensions(self.region, self.proxy_id) - result = vm.add_extensions(extensions) - if isinstance(result, Error): - self.set_failed(result) - return - elif result: - self.state = VmState.running - - self.save() - - def stopping(self) -> None: - vm = self.get_vm() - if not vm.is_deleted(): - logging.info(PROXY_LOG_PREFIX + "stopping proxy: %s", self.region) - vm.delete() - self.save() - else: - self.stopped() - - def stopped(self) -> None: - logging.info(PROXY_LOG_PREFIX + "removing proxy: %s", self.region) - self.delete() - - def is_outdated(self) -> bool: - if self.version != __version__: - logging.info( - PROXY_LOG_PREFIX + "mismatch version: proxy:%s service:%s state:%s", - self.version, - __version__, - self.state, - ) - self.outdated = True - return True - if self.created_timestamp is not None: - proxy_timestamp = self.created_timestamp - if proxy_timestamp < ( - datetime.datetime.now(tz=datetime.timezone.utc) - - datetime.timedelta(days=7) - ): - logging.info( - PROXY_LOG_PREFIX - + "proxy older than 7 days:proxy-created:%s state:%s", - self.created_timestamp, - self.state, - ) - self.outdated = True - return True - return False - - def is_used(self) -> bool: - if len(self.get_forwards()) == 0: - logging.info(PROXY_LOG_PREFIX + "no forwards: %s", self.region) - return False - return True - - def is_alive(self) -> bool: - # Unfortunately, with and without TZ information is required for compare - # or exceptions are generated - ten_minutes_ago_no_tz = datetime.datetime.utcnow() - datetime.timedelta( - minutes=10 - ) - ten_minutes_ago = ten_minutes_ago_no_tz.astimezone(datetime.timezone.utc) - if ( - self.heartbeat is not None - and self.heartbeat.timestamp < ten_minutes_ago_no_tz - ): - logging.error( - PROXY_LOG_PREFIX + "last heartbeat is more than an 10 minutes old: " - "%s - last heartbeat:%s compared_to:%s", - self.region, - self.heartbeat, - ten_minutes_ago_no_tz, - ) - return False - - elif not self.heartbeat and self.timestamp and self.timestamp < ten_minutes_ago: - logging.error( - PROXY_LOG_PREFIX + "no heartbeat in the last 10 minutes: " - "%s timestamp: %s compared_to:%s", - self.region, - self.timestamp, - ten_minutes_ago, - ) - return False - - return True - - def get_forwards(self) -> List[Forward]: - forwards: List[Forward] = [] - for entry in ProxyForward.search_forward( - region=self.region, proxy_id=self.proxy_id - ): - if entry.endtime < datetime.datetime.now(tz=datetime.timezone.utc): - entry.delete() - else: - forwards.append( - Forward( - src_port=entry.port, - dst_ip=entry.dst_ip, - dst_port=entry.dst_port, - ) - ) - return forwards - - def save_proxy_config(self) -> None: - forwards = self.get_forwards() - proxy_config = ProxyConfig( - url=get_file_sas_url( - Container("proxy-configs"), - "%s/%s/config.json" % (self.region, self.proxy_id), - StorageType.config, - read=True, - ), - notification=get_queue_sas( - "proxy", - StorageType.config, - add=True, - ), - forwards=forwards, - region=self.region, - proxy_id=self.proxy_id, - instance_telemetry_key=os.environ.get("APPINSIGHTS_INSTRUMENTATIONKEY"), - microsoft_telemetry_key=os.environ.get("ONEFUZZ_TELEMETRY"), - instance_id=get_instance_id(), - ) - - save_blob( - Container("proxy-configs"), - "%s/%s/config.json" % (self.region, self.proxy_id), - proxy_config.json(), - StorageType.config, - ) - - @classmethod - def search_states(cls, *, states: Optional[List[VmState]] = None) -> List["Proxy"]: - query: QueryFilter = {} - if states: - query["state"] = states - return cls.search(query=query) - - @classmethod - def get_or_create(cls, region: Region) -> Optional["Proxy"]: - proxy_list = Proxy.search( - query={"region": [region], "outdated": [False]}, num_results=1 - ) - proxy_timestamp = None - if len(proxy_list) != 0: - proxy = proxy_list[0] - if proxy.version != __version__: - logging.info( - PROXY_LOG_PREFIX - + "mismatch version: proxy :%s service:%s state:%s", - proxy.version, - __version__, - proxy.state, - ) - if proxy.state != VmState.stopping and not proxy.is_used(): - # If the proxy is out-of-date, delete and re-create it - proxy.state = VmState.stopping - proxy.outdated = True - proxy.save() - return None - if proxy.created_timestamp is not None: - proxy_timestamp = proxy.created_timestamp - if proxy_timestamp < ( - datetime.datetime.now(tz=datetime.timezone.utc) - - datetime.timedelta(days=7) - ): - logging.info( - PROXY_LOG_PREFIX - + "proxy older than 7 days: proxy-created:%s state:%s", - proxy.created_timestamp, - proxy.state, - ) - if proxy.state != VmState.stopping and not proxy.is_used(): - # If the proxy is out-of-date, delete and re-create it - proxy.state = VmState.stopping - proxy.outdated = True - proxy.save() - return None - return proxy - - logging.info(PROXY_LOG_PREFIX + "creating proxy : region:%s", region) - proxy = Proxy(region=region) - proxy.save() - send_event(EventProxyCreated(region=region, proxy_id=proxy.proxy_id)) - return proxy - - def delete(self) -> None: - super().delete() - send_event(EventProxyDeleted(region=self.region, proxy_id=self.proxy_id)) +#!/usr/bin/env python +# +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +import datetime +import logging +import os +from typing import List, Optional, Tuple +from uuid import UUID, uuid4 + +from azure.mgmt.compute.models import VirtualMachine +from onefuzztypes.enums import ErrorCode, VmState +from onefuzztypes.events import EventProxyCreated, EventProxyDeleted, EventProxyFailed +from onefuzztypes.models import ( + Authentication, + Error, + Forward, + ProxyConfig, + ProxyHeartbeat, +) +from onefuzztypes.primitives import Container, Region +from pydantic import Field + +from .__version__ import __version__ +from .azure.auth import build_auth +from .azure.containers import get_file_sas_url, save_blob +from .azure.creds import get_instance_id +from .azure.ip import get_public_ip +from .azure.queue import get_queue_sas +from .azure.storage import StorageType +from .azure.vm import VM +from .events import send_event +from .extension import proxy_manager_extensions +from .orm import ORMMixin, QueryFilter +from .proxy_forward import ProxyForward + +PROXY_SKU = "Standard_B2s" +PROXY_IMAGE = "Canonical:UbuntuServer:18.04-LTS:latest" +PROXY_LOG_PREFIX = "scaleset-proxy: " + + +# This isn't intended to ever be shared to the client, hence not being in +# onefuzztypes +class Proxy(ORMMixin): + timestamp: Optional[datetime.datetime] = Field(alias="Timestamp") + created_timestamp: datetime.datetime = Field( + default_factory=datetime.datetime.utcnow + ) + proxy_id: UUID = Field(default_factory=uuid4) + region: Region + state: VmState = Field(default=VmState.init) + auth: Authentication = Field(default_factory=build_auth) + ip: Optional[str] + error: Optional[Error] + version: str = Field(default=__version__) + heartbeat: Optional[ProxyHeartbeat] + outdated: bool = Field(default=False) + + @classmethod + def key_fields(cls) -> Tuple[str, Optional[str]]: + return ("region", "proxy_id") + + def get_vm(self) -> VM: + vm = VM( + name="proxy-%s-%s" % (self.region, self.proxy_id), + region=self.region, + sku=PROXY_SKU, + image=PROXY_IMAGE, + auth=self.auth, + ) + return vm + + def init(self) -> None: + vm = self.get_vm() + vm_data = vm.get() + if vm_data: + if vm_data.provisioning_state == "Failed": + self.set_provision_failed(vm_data) + return + else: + self.save_proxy_config() + self.state = VmState.extensions_launch + else: + result = vm.create() + if isinstance(result, Error): + self.set_failed(result) + return + self.save() + + def set_provision_failed(self, vm_data: VirtualMachine) -> None: + errors = ["provisioning failed"] + for status in vm_data.instance_view.statuses: + if status.level.name.lower() == "error": + errors.append( + f"code:{status.code} status:{status.display_status} " + f"message:{status.message}" + ) + + self.set_failed( + Error( + code=ErrorCode.PROXY_FAILED, + errors=errors, + ) + ) + return + + def set_failed(self, error: Error) -> None: + if self.error is not None: + return + + logging.error(PROXY_LOG_PREFIX + "vm failed: %s - %s", self.region, error) + send_event( + EventProxyFailed(region=self.region, proxy_id=self.proxy_id, error=error) + ) + self.error = error + self.state = VmState.stopping + self.save() + + def extensions_launch(self) -> None: + vm = self.get_vm() + vm_data = vm.get() + if not vm_data: + self.set_failed( + Error( + code=ErrorCode.PROXY_FAILED, + errors=["azure not able to find vm"], + ) + ) + return + + if vm_data.provisioning_state == "Failed": + self.set_provision_failed(vm_data) + return + + ip = get_public_ip(vm_data.network_profile.network_interfaces[0].id) + if ip is None: + self.save() + return + self.ip = ip + + extensions = proxy_manager_extensions(self.region, self.proxy_id) + result = vm.add_extensions(extensions) + if isinstance(result, Error): + self.set_failed(result) + return + elif result: + self.state = VmState.running + + self.save() + + def stopping(self) -> None: + vm = self.get_vm() + if not vm.is_deleted(): + logging.info(PROXY_LOG_PREFIX + "stopping proxy: %s", self.region) + vm.delete() + self.save() + else: + self.stopped() + + def stopped(self) -> None: + logging.info(PROXY_LOG_PREFIX + "removing proxy: %s", self.region) + self.delete() + + def is_outdated(self) -> bool: + if self.version != __version__: + logging.info( + PROXY_LOG_PREFIX + "mismatch version: proxy:%s service:%s state:%s", + self.version, + __version__, + self.state, + ) + self.outdated = True + return True + if self.created_timestamp is not None: + proxy_timestamp = self.created_timestamp + if proxy_timestamp < ( + datetime.datetime.now(tz=datetime.timezone.utc) + - datetime.timedelta(days=7) + ): + logging.info( + PROXY_LOG_PREFIX + + "proxy older than 7 days:proxy-created:%s state:%s", + self.created_timestamp, + self.state, + ) + self.outdated = True + return True + return False + + def is_used(self) -> bool: + if len(self.get_forwards()) == 0: + logging.info(PROXY_LOG_PREFIX + "no forwards: %s", self.region) + return False + return True + + def is_alive(self) -> bool: + # Unfortunately, with and without TZ information is required for compare + # or exceptions are generated + ten_minutes_ago_no_tz = datetime.datetime.utcnow() - datetime.timedelta( + minutes=10 + ) + ten_minutes_ago = ten_minutes_ago_no_tz.astimezone(datetime.timezone.utc) + if ( + self.heartbeat is not None + and self.heartbeat.timestamp < ten_minutes_ago_no_tz + ): + logging.error( + PROXY_LOG_PREFIX + "last heartbeat is more than an 10 minutes old: " + "%s - last heartbeat:%s compared_to:%s", + self.region, + self.heartbeat, + ten_minutes_ago_no_tz, + ) + return False + + elif not self.heartbeat and self.timestamp and self.timestamp < ten_minutes_ago: + logging.error( + PROXY_LOG_PREFIX + "no heartbeat in the last 10 minutes: " + "%s timestamp: %s compared_to:%s", + self.region, + self.timestamp, + ten_minutes_ago, + ) + return False + + return True + + def get_forwards(self) -> List[Forward]: + forwards: List[Forward] = [] + for entry in ProxyForward.search_forward( + region=self.region, proxy_id=self.proxy_id + ): + if entry.endtime < datetime.datetime.now(tz=datetime.timezone.utc): + entry.delete() + else: + forwards.append( + Forward( + src_port=entry.port, + dst_ip=entry.dst_ip, + dst_port=entry.dst_port, + ) + ) + return forwards + + def save_proxy_config(self) -> None: + forwards = self.get_forwards() + proxy_config = ProxyConfig( + url=get_file_sas_url( + Container("proxy-configs"), + "%s/%s/config.json" % (self.region, self.proxy_id), + StorageType.config, + read=True, + ), + notification=get_queue_sas( + "proxy", + StorageType.config, + add=True, + ), + forwards=forwards, + region=self.region, + proxy_id=self.proxy_id, + instance_telemetry_key=os.environ.get("APPINSIGHTS_INSTRUMENTATIONKEY"), + microsoft_telemetry_key=os.environ.get("ONEFUZZ_TELEMETRY"), + instance_id=get_instance_id(), + ) + + save_blob( + Container("proxy-configs"), + "%s/%s/config.json" % (self.region, self.proxy_id), + proxy_config.json(), + StorageType.config, + ) + + @classmethod + def search_states(cls, *, states: Optional[List[VmState]] = None) -> List["Proxy"]: + query: QueryFilter = {} + if states: + query["state"] = states + return cls.search(query=query) + + @classmethod + def get_or_create(cls, region: Region) -> Optional["Proxy"]: + proxy_list = Proxy.search( + query={"region": [region], "outdated": [False]}, num_results=1 + ) + proxy_timestamp = None + if len(proxy_list) != 0: + proxy = proxy_list[0] + if proxy.version != __version__: + logging.info( + PROXY_LOG_PREFIX + + "mismatch version: proxy :%s service:%s state:%s", + proxy.version, + __version__, + proxy.state, + ) + if proxy.state != VmState.stopping and not proxy.is_used(): + # If the proxy is out-of-date, delete and re-create it + proxy.state = VmState.stopping + proxy.outdated = True + proxy.save() + return None + if proxy.created_timestamp is not None: + proxy_timestamp = proxy.created_timestamp + if proxy_timestamp < ( + datetime.datetime.now(tz=datetime.timezone.utc) + - datetime.timedelta(days=7) + ): + logging.info( + PROXY_LOG_PREFIX + + "proxy older than 7 days: proxy-created:%s state:%s", + proxy.created_timestamp, + proxy.state, + ) + if proxy.state != VmState.stopping and not proxy.is_used(): + # If the proxy is out-of-date, delete and re-create it + proxy.state = VmState.stopping + proxy.outdated = True + proxy.save() + return None + return proxy + + logging.info(PROXY_LOG_PREFIX + "creating proxy : region:%s", region) + proxy = Proxy(region=region) + proxy.save() + send_event(EventProxyCreated(region=region, proxy_id=proxy.proxy_id)) + return proxy + + def delete(self) -> None: + super().delete() + send_event(EventProxyDeleted(region=self.region, proxy_id=self.proxy_id)) From 07d5b2c4f20a2e2b267eeab6f48bb30f2a0d06b1 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 18 May 2021 10:05:50 -0700 Subject: [PATCH 091/110] Changes based of initial review. --- src/api-service/__app__/onefuzzlib/proxy.py | 5 ++--- .../__app__/timer_daily/__init__.py | 18 +++++++++++------- src/ci/onefuzztypes.sh | 2 +- src/pytypes/onefuzztypes/events.py | 6 +++--- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index cf4b651628..70ba062e08 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -38,6 +38,7 @@ PROXY_SKU = "Standard_B2s" PROXY_IMAGE = "Canonical:UbuntuServer:18.04-LTS:latest" PROXY_LOG_PREFIX = "scaleset-proxy: " +PROXY_LIFESPAN = 7 # days # This isn't intended to ever be shared to the client, hence not being in @@ -170,13 +171,12 @@ def is_outdated(self) -> bool: __version__, self.state, ) - self.outdated = True return True if self.created_timestamp is not None: proxy_timestamp = self.created_timestamp if proxy_timestamp < ( datetime.datetime.now(tz=datetime.timezone.utc) - - datetime.timedelta(days=7) + - datetime.timedelta(days=PROXY_LIFESPAN) ): logging.info( PROXY_LOG_PREFIX @@ -184,7 +184,6 @@ def is_outdated(self) -> bool: self.created_timestamp, self.state, ) - self.outdated = True return True return False diff --git a/src/api-service/__app__/timer_daily/__init__.py b/src/api-service/__app__/timer_daily/__init__.py index 0054e00701..df42467e64 100644 --- a/src/api-service/__app__/timer_daily/__init__.py +++ b/src/api-service/__app__/timer_daily/__init__.py @@ -16,16 +16,20 @@ def main(mytimer: func.TimerRequest, dashboard: func.Out[str]) -> None: # noqa: F841 - for proxy in Proxy.search(): + proxy_list = Proxy.search() + for proxy in proxy_list: if proxy.is_outdated(): - logging.info("marking proxy as outdated.") + logging.info("marking proxy in %s as outdated.", proxy.region) proxy.outdated = True proxy.save() - if ( - len(Proxy.search(query={"region": [proxy.region], "outdated": [False]})) - == 0 - ): - logging.info("outdated proxy, creating new one.") + region_list = list( + filter( + lambda x: (x.region == proxy.region and x.outdated == False), + proxy_list, + ) + ) + if not len(region_list): + logging.info("outdated proxy in %s, creating new one.", proxy.region) new_proxy = Proxy(region=proxy.region) new_proxy.save() send_event( diff --git a/src/ci/onefuzztypes.sh b/src/ci/onefuzztypes.sh index 22b2abf56b..0de68afe6c 100755 --- a/src/ci/onefuzztypes.sh +++ b/src/ci/onefuzztypes.sh @@ -21,7 +21,7 @@ pytest -v tests cp dist/*.* ../../artifacts/sdk -echo 'verify webhook docs are up-to-date' +echo 'verify webhook docs are up-to-date' python -m venv build-docs . build-docs/bin/activate pip install -e . diff --git a/src/pytypes/onefuzztypes/events.py b/src/pytypes/onefuzztypes/events.py index df50372cdf..f0971af1ba 100644 --- a/src/pytypes/onefuzztypes/events.py +++ b/src/pytypes/onefuzztypes/events.py @@ -121,17 +121,17 @@ class EventPoolCreated(BaseEvent): class EventProxyCreated(BaseEvent): region: Region - proxy_id: UUID + proxy_id: Optional[UUID] class EventProxyDeleted(BaseEvent): region: Region - proxy_id: UUID + proxy_id: Optional[UUID] class EventProxyFailed(BaseEvent): region: Region - proxy_id: UUID + proxy_id: Optional[UUID] error: Error From e5b1f88dbab435ab35b2a7b4ddced29b25537839 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 18 May 2021 10:06:59 -0700 Subject: [PATCH 092/110] Removing outdated line. --- src/api-service/__app__/onefuzzlib/tasks/main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/tasks/main.py b/src/api-service/__app__/onefuzzlib/tasks/main.py index b78d6b4364..ca4c127310 100644 --- a/src/api-service/__app__/onefuzzlib/tasks/main.py +++ b/src/api-service/__app__/onefuzzlib/tasks/main.py @@ -124,7 +124,6 @@ def init(self) -> None: def stopping(self) -> None: logging.info("stopping task: %s:%s", self.job_id, self.task_id) - delete_queue(str(self.task_id), StorageType.corpus) Node.stop_task(self.task_id) if not NodeTasks.get_nodes_by_task_id(self.task_id): self.stopped() From c116d863b57d817eb0b8b7b0a348214c1aef617d Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 18 May 2021 10:10:18 -0700 Subject: [PATCH 093/110] Updating webhook_events.md --- docs/webhook_events.md | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/docs/webhook_events.md b/docs/webhook_events.md index 61e3b5b991..49f89d1f6d 100644 --- a/docs/webhook_events.md +++ b/docs/webhook_events.md @@ -1276,8 +1276,7 @@ Each event will be submitted via HTTP POST to the user provided URL. } }, "required": [ - "region", - "proxy_id" + "region" ], "title": "EventProxyCreated", "type": "object" @@ -1311,8 +1310,7 @@ Each event will be submitted via HTTP POST to the user provided URL. } }, "required": [ - "region", - "proxy_id" + "region" ], "title": "EventProxyDeleted", "type": "object" @@ -1406,7 +1404,6 @@ Each event will be submitted via HTTP POST to the user provided URL. }, "required": [ "region", - "proxy_id", "error" ], "title": "EventProxyFailed", @@ -4921,8 +4918,7 @@ Each event will be submitted via HTTP POST to the user provided URL. } }, "required": [ - "region", - "proxy_id" + "region" ], "title": "EventProxyCreated", "type": "object" @@ -4940,8 +4936,7 @@ Each event will be submitted via HTTP POST to the user provided URL. } }, "required": [ - "region", - "proxy_id" + "region" ], "title": "EventProxyDeleted", "type": "object" @@ -4963,7 +4958,6 @@ Each event will be submitted via HTTP POST to the user provided URL. }, "required": [ "region", - "proxy_id", "error" ], "title": "EventProxyFailed", From 02c1ae703ab288204a5e02f016be027fa8af0f7a Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 18 May 2021 13:05:32 -0700 Subject: [PATCH 094/110] Refactoring timer_daily proxy check. --- src/api-service/__app__/timer_daily/__init__.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/api-service/__app__/timer_daily/__init__.py b/src/api-service/__app__/timer_daily/__init__.py index df42467e64..6e71efea84 100644 --- a/src/api-service/__app__/timer_daily/__init__.py +++ b/src/api-service/__app__/timer_daily/__init__.py @@ -17,11 +17,15 @@ def main(mytimer: func.TimerRequest, dashboard: func.Out[str]) -> None: # noqa: F841 proxy_list = Proxy.search() + # Marking Outdated Proxies. Subsequently, shutting down Outdated & Unused Proxies. for proxy in proxy_list: if proxy.is_outdated(): logging.info("marking proxy in %s as outdated.", proxy.region) proxy.outdated = True proxy.save() + # Creating a new proxy if no proxy exists for a given region. + for proxy in proxy_list: + if proxy.outdated: region_list = list( filter( lambda x: (x.region == proxy.region and x.outdated == False), @@ -35,10 +39,10 @@ def main(mytimer: func.TimerRequest, dashboard: func.Out[str]) -> None: # noqa: send_event( EventProxyCreated(region=proxy.region, proxy_id=proxy.proxy_id) ) - if not proxy.is_used(): - logging.info("stopping proxy") - proxy.state = VmState.stopping - proxy.save() + if not proxy.is_used(): + logging.info("stopping one proxy in %s.", proxy.region) + proxy.state = VmState.stopping + proxy.save() scalesets = Scaleset.search() for scaleset in scalesets: From 9b6efb96ca8e330edb13329d41b2d2227773d862 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 18 May 2021 13:23:05 -0700 Subject: [PATCH 095/110] Changing lifespan back to minutes for testing. --- src/api-service/__app__/onefuzzlib/proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 70ba062e08..58ba46f80b 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -176,7 +176,7 @@ def is_outdated(self) -> bool: proxy_timestamp = self.created_timestamp if proxy_timestamp < ( datetime.datetime.now(tz=datetime.timezone.utc) - - datetime.timedelta(days=PROXY_LIFESPAN) + - datetime.timedelta(minutes=PROXY_LIFESPAN) ): logging.info( PROXY_LOG_PREFIX From a72308fdc2423c3c71403677403f0125b3571bed Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Tue, 18 May 2021 13:50:26 -0700 Subject: [PATCH 096/110] Fixing lambda. --- src/api-service/__app__/timer_daily/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/timer_daily/__init__.py b/src/api-service/__app__/timer_daily/__init__.py index 6e71efea84..11f794c657 100644 --- a/src/api-service/__app__/timer_daily/__init__.py +++ b/src/api-service/__app__/timer_daily/__init__.py @@ -28,7 +28,7 @@ def main(mytimer: func.TimerRequest, dashboard: func.Out[str]) -> None: # noqa: if proxy.outdated: region_list = list( filter( - lambda x: (x.region == proxy.region and x.outdated == False), + lambda x: (x.region == proxy.region and not x.outdated), proxy_list, ) ) From 12cc82454c7f6ff10aea7d71bc3e3a581bab0859 Mon Sep 17 00:00:00 2001 From: bmc-msft <41130664+bmc-msft@users.noreply.github.com> Date: Wed, 19 May 2021 13:25:34 -0400 Subject: [PATCH 097/110] Apply suggestions from code review --- src/api-service/__app__/onefuzzlib/proxy.py | 4 ++-- src/ci/api-service-toolcheck.sh | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 58ba46f80b..14154f27b4 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -289,7 +289,7 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: if proxy.version != __version__: logging.info( PROXY_LOG_PREFIX - + "mismatch version: proxy :%s service:%s state:%s", + + "mismatch version: proxy :%s service:%s state:%s", proxy.version, __version__, proxy.state, @@ -320,7 +320,7 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: return None return proxy - logging.info(PROXY_LOG_PREFIX + "creating proxy : region:%s", region) + logging.info(PROXY_LOG_PREFIX + "creating proxy: region:%s", region) proxy = Proxy(region=region) proxy.save() send_event(EventProxyCreated(region=region, proxy_id=proxy.proxy_id)) diff --git a/src/ci/api-service-toolcheck.sh b/src/ci/api-service-toolcheck.sh index eeed76630f..2405dd28a6 100644 --- a/src/ci/api-service-toolcheck.sh +++ b/src/ci/api-service-toolcheck.sh @@ -8,11 +8,10 @@ set -ex cd src/api-service pip install -r requirements-dev.txt -# pip install -r requirements-lint.txt black ./__app__ --check flake8 ./__app__ bandit -r ./__app__ isort --profile black ./__app__ --check mypy ./__app__ --ignore-missing-imports -pytest -v tests \ No newline at end of file +pytest -v tests From c21b241641b3ccd749c47f2648ee1f2a0d11727f Mon Sep 17 00:00:00 2001 From: bmc-msft <41130664+bmc-msft@users.noreply.github.com> Date: Wed, 19 May 2021 14:41:35 -0400 Subject: [PATCH 098/110] Apply suggestions from code review --- src/api-service/__app__/onefuzzlib/proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 14154f27b4..4c54e32405 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -284,7 +284,7 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: query={"region": [region], "outdated": [False]}, num_results=1 ) proxy_timestamp = None - if len(proxy_list) != 0: + if proxy_list: proxy = proxy_list[0] if proxy.version != __version__: logging.info( From 5f5bdccbbe6cc5727b0af7097aa55b1c7d344d90 Mon Sep 17 00:00:00 2001 From: bmc-msft <41130664+bmc-msft@users.noreply.github.com> Date: Wed, 19 May 2021 14:44:40 -0400 Subject: [PATCH 099/110] Apply suggestions from code review --- src/api-service/__app__/onefuzzlib/proxy.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 4c54e32405..822a887830 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -284,8 +284,7 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: query={"region": [region], "outdated": [False]}, num_results=1 ) proxy_timestamp = None - if proxy_list: - proxy = proxy_list[0] + for proxy in proxy_list: if proxy.version != __version__: logging.info( PROXY_LOG_PREFIX @@ -299,7 +298,7 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: proxy.state = VmState.stopping proxy.outdated = True proxy.save() - return None + continue if proxy.created_timestamp is not None: proxy_timestamp = proxy.created_timestamp if proxy_timestamp < ( @@ -317,7 +316,7 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: proxy.state = VmState.stopping proxy.outdated = True proxy.save() - return None + continue return proxy logging.info(PROXY_LOG_PREFIX + "creating proxy: region:%s", region) From 2df8272d780b0108e52839e5df043aea5cef5437 Mon Sep 17 00:00:00 2001 From: bmc-msft <41130664+bmc-msft@users.noreply.github.com> Date: Wed, 19 May 2021 14:48:56 -0400 Subject: [PATCH 100/110] Apply suggestions from code review --- src/api-service/__app__/onefuzzlib/proxy.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 822a887830..dc36315d1f 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -285,7 +285,10 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: ) proxy_timestamp = None for proxy in proxy_list: - if proxy.version != __version__: + if proxy.is_oudated(): + proxy.outdated = True + proxy.save() + continue logging.info( PROXY_LOG_PREFIX + "mismatch version: proxy :%s service:%s state:%s", From 94c703ad702523574e61184c66c3ad620eaea831 Mon Sep 17 00:00:00 2001 From: bmc-msft <41130664+bmc-msft@users.noreply.github.com> Date: Wed, 19 May 2021 14:52:23 -0400 Subject: [PATCH 101/110] Apply suggestions from code review --- src/api-service/__app__/onefuzzlib/proxy.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index dc36315d1f..92654a82dd 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -299,26 +299,6 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: if proxy.state != VmState.stopping and not proxy.is_used(): # If the proxy is out-of-date, delete and re-create it proxy.state = VmState.stopping - proxy.outdated = True - proxy.save() - continue - if proxy.created_timestamp is not None: - proxy_timestamp = proxy.created_timestamp - if proxy_timestamp < ( - datetime.datetime.now(tz=datetime.timezone.utc) - - datetime.timedelta(days=7) - ): - logging.info( - PROXY_LOG_PREFIX - + "proxy older than 7 days: proxy-created:%s state:%s", - proxy.created_timestamp, - proxy.state, - ) - if proxy.state != VmState.stopping and not proxy.is_used(): - # If the proxy is out-of-date, delete and re-create it - proxy.state = VmState.stopping - proxy.outdated = True - proxy.save() continue return proxy From 773c1c8d2d80e7ee1804b9df61c41f4b7a722f29 Mon Sep 17 00:00:00 2001 From: bmc-msft <41130664+bmc-msft@users.noreply.github.com> Date: Wed, 19 May 2021 14:53:09 -0400 Subject: [PATCH 102/110] Apply suggestions from code review --- src/api-service/__app__/onefuzzlib/proxy.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 92654a82dd..9ec500e79d 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -289,13 +289,6 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: proxy.outdated = True proxy.save() continue - logging.info( - PROXY_LOG_PREFIX - + "mismatch version: proxy :%s service:%s state:%s", - proxy.version, - __version__, - proxy.state, - ) if proxy.state != VmState.stopping and not proxy.is_used(): # If the proxy is out-of-date, delete and re-create it proxy.state = VmState.stopping From eaa7a07a4510c27f1680b4ffc13fe015ad33bbec Mon Sep 17 00:00:00 2001 From: bmc-msft <41130664+bmc-msft@users.noreply.github.com> Date: Wed, 19 May 2021 14:56:17 -0400 Subject: [PATCH 103/110] Apply suggestions from code review --- src/api-service/__app__/onefuzzlib/proxy.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 9ec500e79d..b4c7ba6eea 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -289,9 +289,8 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: proxy.outdated = True proxy.save() continue - if proxy.state != VmState.stopping and not proxy.is_used(): - # If the proxy is out-of-date, delete and re-create it - proxy.state = VmState.stopping + if proxy.state not in VmState.available(): + continue continue return proxy From e25df0f8745c456cfb9a610ff0ee717340295609 Mon Sep 17 00:00:00 2001 From: bmc-msft <41130664+bmc-msft@users.noreply.github.com> Date: Wed, 19 May 2021 14:56:36 -0400 Subject: [PATCH 104/110] Apply suggestions from code review --- src/api-service/__app__/onefuzzlib/proxy.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index b4c7ba6eea..efacffa6d2 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -291,7 +291,6 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: continue if proxy.state not in VmState.available(): continue - continue return proxy logging.info(PROXY_LOG_PREFIX + "creating proxy: region:%s", region) From e8f196aae98028bf399aa2f58b6c319e39e80ddd Mon Sep 17 00:00:00 2001 From: bmc-msft <41130664+bmc-msft@users.noreply.github.com> Date: Wed, 19 May 2021 15:05:39 -0400 Subject: [PATCH 105/110] Update src/api-service/__app__/onefuzzlib/proxy.py --- src/api-service/__app__/onefuzzlib/proxy.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index efacffa6d2..e379a1580a 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -283,7 +283,6 @@ def get_or_create(cls, region: Region) -> Optional["Proxy"]: proxy_list = Proxy.search( query={"region": [region], "outdated": [False]}, num_results=1 ) - proxy_timestamp = None for proxy in proxy_list: if proxy.is_oudated(): proxy.outdated = True From 271f125aa100ebe8c96650ec1596a18d4a6ead59 Mon Sep 17 00:00:00 2001 From: bmc-msft <41130664+bmc-msft@users.noreply.github.com> Date: Wed, 19 May 2021 15:15:43 -0400 Subject: [PATCH 106/110] Update src/api-service/__app__/onefuzzlib/proxy.py --- src/api-service/__app__/onefuzzlib/proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index e379a1580a..70ba042064 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -281,7 +281,7 @@ def search_states(cls, *, states: Optional[List[VmState]] = None) -> List["Proxy @classmethod def get_or_create(cls, region: Region) -> Optional["Proxy"]: proxy_list = Proxy.search( - query={"region": [region], "outdated": [False]}, num_results=1 + query={"region": [region], "outdated": [False]} ) for proxy in proxy_list: if proxy.is_oudated(): From 81aca12e7bc50499e69cddaf123ce39b235df3a4 Mon Sep 17 00:00:00 2001 From: bmc-msft <41130664+bmc-msft@users.noreply.github.com> Date: Wed, 19 May 2021 15:25:58 -0400 Subject: [PATCH 107/110] Apply suggestions from code review --- src/api-service/__app__/onefuzzlib/proxy.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 70ba042064..c3d1f58c0e 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -280,11 +280,9 @@ def search_states(cls, *, states: Optional[List[VmState]] = None) -> List["Proxy @classmethod def get_or_create(cls, region: Region) -> Optional["Proxy"]: - proxy_list = Proxy.search( - query={"region": [region], "outdated": [False]} - ) + proxy_list = Proxy.search(query={"region": [region], "outdated": [False]}) for proxy in proxy_list: - if proxy.is_oudated(): + if proxy.is_outdated(): proxy.outdated = True proxy.save() continue From 1fee5423b46cec7fb658897092a0ebb9b1db8005 Mon Sep 17 00:00:00 2001 From: Noah Harper Date: Wed, 19 May 2021 13:50:53 -0700 Subject: [PATCH 108/110] Changing 'minutes' back to 'days.' --- src/api-service/__app__/onefuzzlib/proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index c3d1f58c0e..437dc711a3 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -176,7 +176,7 @@ def is_outdated(self) -> bool: proxy_timestamp = self.created_timestamp if proxy_timestamp < ( datetime.datetime.now(tz=datetime.timezone.utc) - - datetime.timedelta(minutes=PROXY_LIFESPAN) + - datetime.timedelta(days=PROXY_LIFESPAN) ): logging.info( PROXY_LOG_PREFIX From 7913c3f2e2ef42f475bd803a5d9b91e98e04df2e Mon Sep 17 00:00:00 2001 From: bmc-msft <41130664+bmc-msft@users.noreply.github.com> Date: Wed, 19 May 2021 16:57:38 -0400 Subject: [PATCH 109/110] Apply suggestions from code review --- src/api-service/__app__/onefuzzlib/proxy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 437dc711a3..70dffdfd65 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -38,7 +38,7 @@ PROXY_SKU = "Standard_B2s" PROXY_IMAGE = "Canonical:UbuntuServer:18.04-LTS:latest" PROXY_LOG_PREFIX = "scaleset-proxy: " -PROXY_LIFESPAN = 7 # days +PROXY_LIFESPAN = datetime.timedelta(days=7) # This isn't intended to ever be shared to the client, hence not being in @@ -176,7 +176,7 @@ def is_outdated(self) -> bool: proxy_timestamp = self.created_timestamp if proxy_timestamp < ( datetime.datetime.now(tz=datetime.timezone.utc) - - datetime.timedelta(days=PROXY_LIFESPAN) + - PROXY_LIFESPAN ): logging.info( PROXY_LOG_PREFIX From db447fb38b01a25d4e63f69bb18da8aa03dfcc82 Mon Sep 17 00:00:00 2001 From: bmc-msft <41130664+bmc-msft@users.noreply.github.com> Date: Wed, 19 May 2021 17:06:26 -0400 Subject: [PATCH 110/110] Update src/api-service/__app__/onefuzzlib/proxy.py --- src/api-service/__app__/onefuzzlib/proxy.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/api-service/__app__/onefuzzlib/proxy.py b/src/api-service/__app__/onefuzzlib/proxy.py index 70dffdfd65..2127f8e824 100644 --- a/src/api-service/__app__/onefuzzlib/proxy.py +++ b/src/api-service/__app__/onefuzzlib/proxy.py @@ -175,8 +175,7 @@ def is_outdated(self) -> bool: if self.created_timestamp is not None: proxy_timestamp = self.created_timestamp if proxy_timestamp < ( - datetime.datetime.now(tz=datetime.timezone.utc) - - PROXY_LIFESPAN + datetime.datetime.now(tz=datetime.timezone.utc) - PROXY_LIFESPAN ): logging.info( PROXY_LOG_PREFIX