Skip to content

Commit de5507d

Browse files
committed
added example for recursive project deletion
1 parent eb88c12 commit de5507d

File tree

2 files changed

+195
-0
lines changed

2 files changed

+195
-0
lines changed
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
'''
2+
Created: Nov 23, 2023
3+
Author: mkumykov
4+
5+
Copyright (c) 2023 - Synopsys, Inc.
6+
7+
Licensed under the Apache License, Version 2.0 (the "License");
8+
you may not use this file except in compliance with the License.
9+
You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.
18+
19+
This script will remove project hierarchies from a Black Duck server.
20+
21+
usage: python3 recursive_delete_project.py [-h] -u BASE_URL -t TOKEN_FILE [-nv] [-p PROJECT_NAME] [-pv VERSION_NAME] [-pl PROJECT_LIST_FILE]
22+
23+
options:
24+
-h, --help show this help message and exit
25+
-u BASE_URL, --base-url BASE_URL
26+
Hub server URL e.g. https://your.blackduck.url
27+
-t TOKEN_FILE, --token-file TOKEN_FILE
28+
File containing access token
29+
-nv, --no-verify Disable TLS certificate verification
30+
-p PROJECT_NAME, --project-name PROJECT_NAME
31+
Project Name
32+
-pv VERSION_NAME, --version-name VERSION_NAME
33+
Version Name
34+
-pl PROJECT_LIST_FILE, --project-list-file PROJECT_LIST_FILE
35+
File containing project name list
36+
37+
Options -pl and -p can not be used at the same time
38+
39+
Examples:
40+
41+
remove project with all sub-projects
42+
43+
python3 recursive_delete_project.py -u BASE_URL -t TOKEN_FILE -nv -p PROJECT_NAME
44+
45+
remove project version with all sub-projects, keep sub-projects still in use intact
46+
47+
python3 recursive_delete_project.py -u BASE_URL -t TOKEN_FILE -nv -p PROJECT_NAME -pv VERSION_NAME
48+
49+
remove projects listed in a file
50+
51+
python3 recursive_delete_project.py -u BASE_URL -t TOKEN_FILE -nv -pl PROJECT_LIST_FILE
52+
53+
'''
54+
55+
import argparse
56+
import json
57+
import logging
58+
import sys
59+
import arrow
60+
61+
from blackduck import Client
62+
from pprint import pprint,pformat
63+
64+
logging.basicConfig(format='%(asctime)s:%(levelname)s:%(message)s', stream=sys.stderr, level=logging.DEBUG)
65+
logging.getLogger("requests").setLevel(logging.INFO)
66+
logging.getLogger("urllib3").setLevel(logging.INFO)
67+
logging.getLogger("blackduck").setLevel(logging.INFO)
68+
69+
strict = False
70+
71+
def remove_project_structure(project_name):
72+
project = find_project_by_name(project_name)
73+
if not project:
74+
logging.info(f"Project {project_name} does not exist.")
75+
return
76+
versions = bd.get_resource('versions', project)
77+
for version in versions:
78+
remove_project_version_structure(project_name, version['versionName'])
79+
80+
def remove_project_version_structure(project_name, version_name):
81+
project = find_project_by_name(project_name)
82+
if not project:
83+
logging.info(f"Project {project_name} does not exist.")
84+
return
85+
num_versions = bd.get_resource('versions', project, items=False)['totalCount']
86+
version = find_project_version_by_name(project,version_name)
87+
if not version:
88+
logging.info(f"Project {project_name} with version {version_name} does not exist.")
89+
return
90+
components = [
91+
c for c in bd.get_resource('components',version) if c['componentType'] == "SUB_PROJECT"
92+
]
93+
logging.info(f"Project {project_name}:{version_name} has {len(components)} subprojects")
94+
for component in components:
95+
component_name = component['componentName']
96+
component_version_name = component['componentVersionName']
97+
logging.info(f"Removing subproject {component_name} from {project_name}:{version_name}")
98+
component_url = component['_meta']['href']
99+
response = bd.session.delete(component_url)
100+
logging.info(f"Operation completed with {response}")
101+
remove_project_version_structure(component_name, component_version_name)
102+
logging.info(f"Removing {project_name}:{version_name}")
103+
if num_versions > 1:
104+
response = bd.session.delete(version['_meta']['href'])
105+
else:
106+
response = bd.session.delete(project['_meta']['href'])
107+
logging.info(f"Operation completed with {response}")
108+
109+
def remove_codelocations_recursively(version):
110+
components = bd.get_resource('components', version)
111+
subprojects = [x for x in components if x['componentType'] == 'SUB_PROJECT']
112+
logging.info(f"Found {len(subprojects)} subprojects")
113+
unmap_all_codelocations(version)
114+
for subproject in subprojects:
115+
subproject_name = subproject['componentName']
116+
subproject_version_name = subproject['componentVersionName']
117+
project = find_project_by_name(subproject_name)
118+
if not project:
119+
logging.info(f"Project {subproject_name} does not exist.")
120+
return
121+
subproject_version = find_project_version_by_name(project, subproject_version_name)
122+
if not subproject_version:
123+
logging.info(f"Project {subproject_name} with version {subversion_name} does not exist.")
124+
return
125+
remove_codelocations_recursively(subproject_version)
126+
127+
def unmap_all_codelocations(version):
128+
codelocations = bd.get_resource('codelocations',version)
129+
for codelocation in codelocations:
130+
logging.info(f"Unmapping codelocation {codelocation['name']}")
131+
codelocation['mappedProjectVersion'] = ""
132+
response = bd.session.put(codelocation['_meta']['href'], json=codelocation)
133+
pprint (response)
134+
135+
def find_project_by_name(project_name):
136+
params = {
137+
'q': [f"name:{project_name}"]
138+
}
139+
projects = [p for p in bd.get_resource('projects', params=params) if p['name'] == project_name]
140+
if len(projects) == 1:
141+
return projects[0]
142+
else:
143+
return None
144+
145+
def find_project_version_by_name(project, version_name):
146+
params = {
147+
'q': [f"versionName:{version_name}"]
148+
}
149+
versions = [v for v in bd.get_resource('versions', project, params=params) if v['versionName'] == version_name]
150+
if len(versions) == 1:
151+
return versions[0]
152+
else:
153+
return None
154+
155+
156+
157+
def parse_command_args():
158+
159+
parser = argparse.ArgumentParser("python3 recursive_delete_project.py")
160+
parser.add_argument("-u", "--base-url", required=True, help="Hub server URL e.g. https://your.blackduck.url")
161+
parser.add_argument("-t", "--token-file", required=True, help="File containing access token")
162+
parser.add_argument("-nv", "--no-verify", action='store_false', help="Disable TLS certificate verification")
163+
group = parser.add_mutually_exclusive_group()
164+
group.add_argument("-p", "--project-name", required=False, help="Project Name")
165+
parser.add_argument("-pv", "--version-name", required=False, help="Version Name")
166+
group.add_argument("-pl", "--project-list-file", required=False, help="File containing project name list")
167+
return parser.parse_args()
168+
169+
def main():
170+
args = parse_command_args()
171+
with open(args.token_file, 'r') as tf:
172+
access_token = tf.readline().strip()
173+
global bd
174+
global scan_params
175+
scan_params = []
176+
bd = Client(base_url=args.base_url, token=access_token, verify=args.no_verify, timeout=60.0, retries=4)
177+
logging.info(f"{args}")
178+
179+
if not (args.project_list_file or args.project_name):
180+
logging.error("Project Name or a File containing Project Names should be specified")
181+
return
182+
183+
if args.project_list_file:
184+
with open(args.project_list_file) as file:
185+
lines = [line.rstrip() for line in file]
186+
for line in lines:
187+
remove_project_structure(line)
188+
elif args.version_name:
189+
remove_project_version_structure(args.project_name, args.version_name)
190+
else:
191+
remove_project_structure(args.project_name)
192+
193+
if __name__ == "__main__":
194+
sys.exit(main())

examples/client/upload_bdio.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ def main():
6565
files = {"file": open(args.filename,"rb")}
6666
response = bd.session.post("/api/scan/data", files = files)
6767
logging.info(response)
68+
logging.info(response.headers)
6869

6970
def parse_command_args():
7071
parser = argparse.ArgumentParser(prog = "upload_bdio", description="Uploads BDIO file to a Blackduck server", epilog="Blackduck examples collection")

0 commit comments

Comments
 (0)