diff --git a/hack/tool/tackle b/hack/tool/tackle index 7e73bb20a..2decde9c9 100755 --- a/hack/tool/tackle +++ b/hack/tool/tackle @@ -35,6 +35,10 @@ parser.add_argument('-n','--no-auth', dest='noAuth', action='store_const', const help='Skip Keycloak token creation, use empty Auth token in Tackle API calls.') parser.add_argument('-b','--skip-buckets', dest='skipBuckets', action='store_const', const=True, default=False, help='Skip Tackle 2 Buckets content export.') +parser.add_argument('-t','--token', type=str, help='Bearer auth token for Hub API (login/password is a fallback to create it).', + nargs='?', default='') +parser.add_argument('-p','--pathfinder-url', type=str, help='In-cluster Pathfinder endpoint URL.', + nargs='?', default='') args = parser.parse_args() ############################################################################### @@ -82,20 +86,24 @@ def debugPrint(str): if args.verbose: print(str) -def getHubToken(host, username, password): - print("Getting auth token via Hub from %s" % host) - url = "%s/hub/auth/login" % host - data = '{"user": "%s", "password": "%s"}' % (username, password) - - r = requests.post(url, data=data, verify=False) - if r.ok: - respData = json.loads(r.text) - debugPrint("Got access token: %s" % respData['token']) - return respData['token'] +def getHubToken(host, username, password, token): + if token: + print("Skipping login, using provided auth token.") + return token else: - print("ERROR getting auth token from %s" % url) - print(data, r) - exit(1) + print("Getting auth token via Hub from %s" % host) + url = "%s/hub/auth/login" % host + data = '{"user": "%s", "password": "%s"}' % (username, password) + + r = requests.post(url, data=data, verify=False) + if r.ok: + respData = json.loads(r.text) + debugPrint("Got access token: %s" % respData['token']) + return respData['token'] + else: + print("ERROR getting auth token from %s" % url) + print(data, r) + exit(1) def getKeycloakToken(host, username, password, client_id='tackle-ui', realm='tackle'): if args.noAuth: @@ -725,72 +733,54 @@ class TackleTool: # Push the updated assessment apiJSON(self.tackle2Url + tackle2path("assessments/%d" % assmnt2['id']), self.tackle2Token, data=assmnt2, method='PATCH', ignoreErrors=ignoreErrors) - def migrateAssessments(self, assessmentsFileName="assessments", ignoreErrors=False): + # Migrate Pathfinder Assessment to Konveyor (expecting Pathfinder hard-coded questionnaire ID=1) + def migrateAssessments(self, pathfinderUrl, ignoreErrors=False): cnt = 0 - ## Export Pathfinder data for each Application - #for app in apiJSON(self.tackle2Url + "/hub/applications", self.tackle2Token): - # for assm2 in apiJSON(self.tackle2Url + "/hub/pathfinder/assessments?applicationId=%d" % app.id, self.tackle2Token): - # # Prepare Assessment and find its ID - # assm = Tackle2Object() - # assm.id = assm2['id'] - # assm.applicationId = assm2['applicationId'] - # assm.status = assm2['status'] - # # Prepare Assessment questions and answers - # asqa2 = apiJSON(self.tackle2Url + "/hub/pathfinder/assessments/%d" % assm.id, self.tackle2Token) - # asqa = Tackle2Object() - # asqa.id = asqa2['id'] - # asqa.applicationId = asqa2['applicationId'] - # asqa.status = asqa2['status'] - # asqa.stakeholders = asqa2['stakeholders'] - # asqa.stakeholderGroups = asqa2['stakeholderGroups'] - # asqa.questionnaire = asqa2['questionnaire'] - # self.add('assessments', asqa) - #saveJSON(os.path.join(self.dataDir, assessmentsFileName), self.data["assessments"]) - - # Upload assessment to Konveyor (expecting Pathfinder hard-coded questionnaire) - dictCollection = loadDump(os.path.join(self.dataDir, assessmentsFileName + '.json')) - print("Processing Pathfinder assessments..") - for passmnt in dictCollection: - print("# Assessment for Application %s" % passmnt["applicationId"]) - appAssessmentsPath = "/hub/applications/%d/assessments" % passmnt["applicationId"] - # Skip if Assessment for given Application already exists - if len(apiJSON(self.tackle2Url + appAssessmentsPath, self.tackle2Token, data={"questionnaire": {"id": 1}})) > 0: - print(" Assessment already exists, skipping.") - continue + apps = apiJSON(self.tackle2Url + "/hub/applications", self.tackle2Token) + print("There are %d Applications, looking for their Assessments.." % len(apps)) + for app in apps: + # Export Pathfinder data for each Application + for passmnt in apiJSON(pathfinderUrl + "/assessments?applicationId=%d" % app['id'], self.tackle2Token): + print("# Assessment for Application %s" % passmnt["applicationId"]) + appAssessmentsPath = "/hub/applications/%d/assessments" % passmnt["applicationId"] + # Skip if Assessment for given Application already exists + if len(apiJSON(self.tackle2Url + appAssessmentsPath, self.tackle2Token, data={"questionnaire": {"id": 1}})) > 0: + print(" Assessment already exists, skipping.") + continue - # Prepare new Assessment - assmnt = dict() - assmnt['questionnaire'] = {"id": 1} # Default new Questionnaire "Pathfinder Legacy" - assmnt['application'] = {"id": passmnt["applicationId"]} - assmnt['stakeholders'] = [] - for sh in passmnt['stakeholders']: - assmnt['stakeholders'].append({"id": sh}) - assmnt['stakeholderGroups'] = [] - for shg in passmnt['stakeholderGroups']: - assmnt['stakeholderGroups'].append({"id": shg}) - - # Transformate Questions, Answers and related structures - for category in passmnt['questionnaire']['categories']: - del category['id'] - category['name'] = category.pop('title') - for question in category["questions"]: - del question['id'] - question["text"] = question.pop('question') - question["explanation"] = question.pop('description') - question["answers"] = question.pop('options') - for answer in question['answers']: - del answer['id'] - answer['text'] = answer.pop('option') - answer['selected'] = answer.pop('checked') - answer['risk'] = answer['risk'].lower() - if answer['risk'] == "amber": - answer['risk'] = "yellow" - assmnt['sections'] = passmnt['questionnaire']['categories'] - - # Post the Assessment - apiJSON(self.tackle2Url + appAssessmentsPath, self.tackle2Token, data=assmnt, method='POST') - cnt += 1 - print("Assessment submitted.") + # Prepare new Assessment + assmnt = dict() + assmnt['questionnaire'] = {"id": 1} # Default new Questionnaire "Pathfinder Legacy" + assmnt['application'] = {"id": passmnt["applicationId"]} + assmnt['stakeholders'] = [] + for sh in passmnt['stakeholders']: + assmnt['stakeholders'].append({"id": sh}) + assmnt['stakeholderGroups'] = [] + for shg in passmnt['stakeholderGroups']: + assmnt['stakeholderGroups'].append({"id": shg}) + + # Transformate Questions, Answers and related structures + for category in passmnt['questionnaire']['categories']: + del category['id'] + category['name'] = category.pop('title') + for question in category["questions"]: + del question['id'] + question["text"] = question.pop('question') + question["explanation"] = question.pop('description') + question["answers"] = question.pop('options') + for answer in question['answers']: + del answer['id'] + answer['text'] = answer.pop('option') + answer['selected'] = answer.pop('checked') + answer['risk'] = answer['risk'].lower() + if answer['risk'] == "amber": + answer['risk'] = "yellow" + assmnt['sections'] = passmnt['questionnaire']['categories'] + + # Post the Assessment + apiJSON(self.tackle2Url + appAssessmentsPath, self.tackle2Token, data=assmnt, method='POST') + cnt += 1 + print("Assessment submitted.") return cnt def preImportCheck(self): @@ -956,7 +946,7 @@ if cmdWanted(args, "export-tackle1"): if cmdWanted(args, "export"): cmdExecuted = True # Gather Keycloak access tokens for Tackle2 - token2 = getHubToken(c['url'], c['username'], c['password']) + token2 = getHubToken(c['url'], c['username'], c['password'], args.token) # Setup data migration object tool = TackleTool(args.data_dir, '', '', c['url'], token2, c['encryption_passphase']) @@ -979,7 +969,7 @@ if cmdWanted(args, "export"): if cmdWanted(args, "import"): cmdExecuted = True # Gather Keycloak access token for Tackle 2 - token2 = getHubToken(c['url'], c['username'], c['password']) + token2 = getHubToken(c['url'], c['username'], c['password'], args.token) # Setup Tackle 1.2->2.0 data migration object tool = TackleTool(args.data_dir, '', '', c['url'], token2, c['encryption_passphase']) @@ -1001,7 +991,7 @@ if cmdWanted(args, "import"): if cmdWanted(args, "clean"): cmdExecuted = True # Gather Keycloak access token for Tackle 2 - token2 = getHubToken(c['url'], c['username'], c['password']) + token2 = getHubToken(c['url'], c['username'], c['password'], args.token) # Setup Tackle 1.2->2.0 data migration object tool = TackleTool(args.data_dir, '', '', c['url'], token2) @@ -1015,7 +1005,7 @@ if cmdWanted(args, "clean"): if cmdWanted(args, "clean-all"): cmdExecuted = True # Gather Keycloak access token for Tackle 2 - token2 = getHubToken(c['url'], c['username'], c['password']) + token2 = getHubToken(c['url'], c['username'], c['password'], args.token) # Setup Tackle 1.2->2.0 data migration object tool = TackleTool(args.data_dir, '', '', c['url'], token2) @@ -1027,15 +1017,21 @@ if cmdWanted(args, "clean-all"): # Migrate Pathfinder Assessments to Konveyor Assessments if cmdWanted(args, "migrate-assessments"): cmdExecuted = True + + # Check Pathfinder URL arg + if not args.pathfinder_url: + print("Error: Pathfinder URL is required, specify it with -p or --pathfinder-url option.") + exit(1) + # Gather Keycloak access token for Tackle 2 - token2 = getHubToken(c['url'], c['username'], c['password']) + token2 = getHubToken(c['url'], c['username'], c['password'], args.token) # Setup Tackle data migration object tool = TackleTool(args.data_dir, '', '', c['url'], token2) # Run the import print("Starting Pathfinder Assessments to Konveyor Assessment migration.") - appCnt = tool.migrateAssessments(ignoreErrors=args.ignoreImportErrors) + appCnt = tool.migrateAssessments(args.pathfinder_url) print("Done. %d new Assessment(s) for Application(s) were migrated!" % appCnt)