1
- import sys
2
- from dataclasses import dataclass
3
- import pathlib
4
- import json
5
1
import datetime
6
- import timeit
2
+ import json
3
+ import pathlib
4
+ import sys
7
5
import time
6
+ import timeit
7
+ from dataclasses import dataclass
8
+ from io import BufferedReader
8
9
9
- from requests import Session , post , get
10
+ from requests import Session , get , post
10
11
from requests .auth import HTTPBasicAuth
11
12
12
13
from contentctl .objects .config import inspect
13
- from contentctl .objects .savedsearches_conf import SavedsearchesConf
14
14
from contentctl .objects .errors import (
15
- MetadataValidationError ,
16
15
DetectionIDError ,
17
16
DetectionMissingError ,
18
- VersionDecrementedError ,
17
+ MetadataValidationError ,
19
18
VersionBumpingError ,
19
+ VersionDecrementedError ,
20
20
)
21
+ from contentctl .objects .savedsearches_conf import SavedsearchesConf
22
+
23
+ """
24
+ The following list includes all appinspect tags available from:
25
+ https://dev.splunk.com/enterprise/reference/appinspect/appinspecttagreference/
26
+
27
+ This allows contentctl to be as forward-leaning as possible in catching
28
+ any potential issues on the widest variety of stacks.
29
+ """
30
+ INCLUDED_TAGS_LIST = [
31
+ "aarch64_compatibility" ,
32
+ "ast" ,
33
+ "cloud" ,
34
+ "future" ,
35
+ "manual" ,
36
+ "packaging_standards" ,
37
+ "private_app" ,
38
+ "private_classic" ,
39
+ "private_victoria" ,
40
+ "splunk_appinspect" ,
41
+ ]
42
+ INCLUDED_TAGS_STRING = "," .join (INCLUDED_TAGS_LIST )
21
43
22
44
23
45
@dataclass (frozen = True )
@@ -28,7 +50,6 @@ class InspectInputDto:
28
50
class Inspect :
29
51
def execute (self , config : inspect ) -> str :
30
52
if config .build_app or config .build_api :
31
- self .inspectAppCLI (config )
32
53
appinspect_token = self .inspectAppAPI (config )
33
54
34
55
if config .enable_metadata_validation :
@@ -49,10 +70,6 @@ def inspectAppAPI(self, config: inspect) -> str:
49
70
session .auth = HTTPBasicAuth (
50
71
config .splunk_api_username , config .splunk_api_password
51
72
)
52
- if config .stack_type not in ["victoria" , "classic" ]:
53
- raise Exception (
54
- f"stack_type MUST be either 'classic' or 'victoria', NOT '{ config .stack_type } '"
55
- )
56
73
57
74
APPINSPECT_API_LOGIN = "https://api.splunk.com/2.0/rest/login/splunk"
58
75
@@ -64,10 +81,6 @@ def inspectAppAPI(self, config: inspect) -> str:
64
81
APPINSPECT_API_VALIDATION_REQUEST = (
65
82
"https://appinspect.splunk.com/v1/app/validate"
66
83
)
67
- headers = {
68
- "Authorization" : f"bearer { authorization_bearer } " ,
69
- "Cache-Control" : "no-cache" ,
70
- }
71
84
72
85
package_path = config .getPackageFilePath (include_version = False )
73
86
if not package_path .is_file ():
@@ -77,18 +90,43 @@ def inspectAppAPI(self, config: inspect) -> str:
77
90
"trying to 'contentctl deploy_acs' the package BEFORE running 'contentctl build'?"
78
91
)
79
92
80
- files = {
93
+ """
94
+ Some documentation on "files" argument for requests.post exists here:
95
+ https://docs.python-requests.org/en/latest/api/
96
+ The type (None, INCLUDED_TAGS_STRING) is intentional, and the None is important.
97
+ In curl syntax, the request we make below is equivalent to
98
+ curl -X POST \
99
+ -H "Authorization: bearer <TOKEN>" \
100
+ -H "Cache-Control: no-cache" \
101
+ -F "app_package=@<PATH/APP-PACKAGE>" \
102
+ -F "included_tags=cloud" \
103
+ --url "https://appinspect.splunk.com/v1/app/validate"
104
+
105
+ This is confirmed by the great resource:
106
+ https://curlconverter.com/
107
+ """
108
+ data : dict [str , tuple [None , str ] | BufferedReader ] = {
81
109
"app_package" : open (package_path , "rb" ),
82
- "included_tags" : (None , "cloud" ),
110
+ "included_tags" : (
111
+ None ,
112
+ INCLUDED_TAGS_STRING ,
113
+ ), # tuple with None is intentional here
83
114
}
84
115
85
- res = post (APPINSPECT_API_VALIDATION_REQUEST , headers = headers , files = files )
116
+ headers = {
117
+ "Authorization" : f"bearer { authorization_bearer } " ,
118
+ "Cache-Control" : "no-cache" ,
119
+ }
120
+
121
+ res = post (APPINSPECT_API_VALIDATION_REQUEST , files = data , headers = headers )
86
122
87
123
res .raise_for_status ()
88
124
89
125
request_id = res .json ().get ("request_id" , None )
90
- APPINSPECT_API_VALIDATION_STATUS = f"https://appinspect.splunk.com/v1/app/validate/status/{ request_id } ?included_tags=private_{ config .stack_type } "
91
- headers = headers = {"Authorization" : f"bearer { authorization_bearer } " }
126
+ APPINSPECT_API_VALIDATION_STATUS = (
127
+ f"https://appinspect.splunk.com/v1/app/validate/status/{ request_id } "
128
+ )
129
+
92
130
startTime = timeit .default_timer ()
93
131
# the first time, wait for 40 seconds. subsequent times, wait for less.
94
132
# this is because appinspect takes some time to return, so there is no sense
@@ -114,7 +152,9 @@ def inspectAppAPI(self, config: inspect) -> str:
114
152
raise Exception (f"Error - Unknown Appinspect API status '{ status } '" )
115
153
116
154
# We have finished running appinspect, so get the report
117
- APPINSPECT_API_REPORT = f"https://appinspect.splunk.com/v1/app/report/{ request_id } ?included_tags=private_{ config .stack_type } "
155
+ APPINSPECT_API_REPORT = (
156
+ f"https://appinspect.splunk.com/v1/app/report/{ request_id } "
157
+ )
118
158
# Get human-readable HTML report
119
159
headers = headers = {
120
160
"Authorization" : f"bearer { authorization_bearer } " ,
@@ -159,14 +199,14 @@ def inspectAppCLI(self, config: inspect) -> None:
159
199
"\t - https://dev.splunk.com/enterprise/docs/developapps/testvalidate/appinspect/useappinspectclitool/"
160
200
)
161
201
from splunk_appinspect .main import (
162
- validate ,
163
- MODE_OPTION ,
164
202
APP_PACKAGE_ARGUMENT ,
165
- OUTPUT_FILE_OPTION ,
166
- LOG_FILE_OPTION ,
167
- INCLUDED_TAGS_OPTION ,
168
203
EXCLUDED_TAGS_OPTION ,
204
+ INCLUDED_TAGS_OPTION ,
205
+ LOG_FILE_OPTION ,
206
+ MODE_OPTION ,
207
+ OUTPUT_FILE_OPTION ,
169
208
TEST_MODE ,
209
+ validate ,
170
210
)
171
211
except Exception as e :
172
212
print (e )
0 commit comments