def import_image( dbsession, account: str, operation_id: str, import_manifest: ImportManifest, force: bool = False, annotations: dict = None, ) -> dict: """ Process the image import finalization, creating the new 'image' record and setting the proper state for queueing :param dbsession: :param account: :param operation_id: :param import_manifest: :param force: :param annotations: :return: """ logger.debug( "Processing import image request with source operation_id = %s, annotations = %s", operation_id, annotations, ) # Add annotation indicating this is an import annotations = add_import_annotations(import_manifest, annotations) # Import analysis for a new digest, or re-load analysis for existing image logger.debug("Loading image info using import operation id %s", operation_id) image_references = [] for t in import_manifest.tags: r = DockerImageReference.from_string(t) r.digest = import_manifest.digest if import_manifest.local_image_id: r.image_id = import_manifest.local_image_id else: r.image_id = import_manifest.digest image_references.append(r) if not (image_references and image_references[0].has_digest()): raise ValueError("Must have image digest in image reference") # Check for dockerfile updates to an existing image found_img = db_catalog_image.get(imageDigest=import_manifest.digest, userId=account, session=dbsession) # Removed this to align processing with how analysis works: the status is updated *after* the add call # if the record already had an older status it will get reset if (found_img and found_img["analysis_status"] not in taskstate.fault_state("analyze") and not force): # Load the existing manifest since we aren't going to use the import manifest for analysis obj_mgr = get_manager() manifest = obj_mgr.get_document(account, "manifest_data", found_img["imageDigest"]) parent_manifest = obj_mgr.get_document(account, "parent_manifest_data", found_img["imageDigest"]) # Don't allow a dockerfile update via import path dockerfile_content = None dockerfile_mode = None # Finalize the import, go straight to complete finalize_import_operation( dbsession, account, operation_id, import_manifest, final_state=ImportState.complete, ) # raise BadRequest( # "Cannot reload image that already exists unless using force=True for re-analysis", # detail={"digest": import_manifest.digest}, # ) else: # Finalize the import internal_import_manifest = finalize_import_operation( dbsession, account, operation_id, import_manifest) # Get the dockerfile content if available if import_manifest.contents.dockerfile: rec = [ ref for ref in internal_import_manifest.contents if ref.content_type == ImportTypes.dockerfile.value ][0] obj_mgr = get_manager() dockerfile_content = obj_mgr.get_document( userId=account, bucket=rec.bucket, archiveId=rec.key, ) dockerfile_mode = "Actual" else: dockerfile_content = "" dockerfile_mode = "Guessed" # Set the manifest to the import manifest. This is swapped out for the real manifest during the import operation on # the analyzer manifest = internal_import_manifest.to_json() parent_manifest = "" # Update the db for the image record image_records = add_or_update_image( dbsession, account, image_references[0].image_id, tags=[x.tag_pullstring() for x in image_references], digests=[x.digest_pullstring() for x in image_references], parentdigest=import_manifest.parent_digest if import_manifest.parent_digest else import_manifest.digest, dockerfile=dockerfile_content, dockerfile_mode=dockerfile_mode, manifest= manifest, # Fo now use the import manifest as the image manifest. This will get set to the actual manifest on the analyzer parent_manifest=parent_manifest, annotations=annotations, ) if image_records: image_record = image_records[0] else: raise Exception("No record updated/inserted") return image_record
def images(request_inputs): user_auth = request_inputs['auth'] method = request_inputs['method'] bodycontent = request_inputs['bodycontent'] params = request_inputs['params'] return_object = {} httpcode = 500 userId, pw = user_auth digest = tag = imageId = imageDigest = dockerfile = None history = False if params and 'history' in params: history = params['history'] force = False if params and 'force' in params: force = params['force'] if bodycontent: jsondata = json.loads(bodycontent) if 'digest' in jsondata: digest = jsondata['digest'] elif 'tag' in jsondata: tag = jsondata['tag'] elif 'imageDigest' in jsondata: imageDigest = jsondata['imageDigest'] elif 'imageId' in jsondata: imageId = jsondata['imageId'] if 'dockerfile' in jsondata: dockerfile = jsondata['dockerfile'] try: if method == 'GET': logger.debug("handling GET: ") try: return_object = [] image_records = catalog.get_image(user_auth, digest=digest, tag=tag, imageId=imageId, imageDigest=imageDigest, history=history) for image_record in image_records: return_object.append(make_response_image(user_auth, image_record, params)) httpcode = 200 except Exception as err: raise err elif method == 'POST': logger.debug("handling POST: ") # if not, add it and set it up to be analyzed if not tag: # dont support digest add, yet httpcode = 500 raise Exception("digest add unsupported") # add the image to the catalog image_record = catalog.add_image(user_auth, tag=tag, dockerfile=dockerfile) imageDigest = image_record['imageDigest'] # finally, do any state updates and return if image_record: #logger.debug("fetched image_info: " + json.dumps(image_record, indent=4)) logger.debug("added image: " + str(imageDigest)) # auto-subscribe for NOW for image_detail in image_record['image_detail']: fulltag = image_detail['registry'] + "/" + image_detail['repo'] + ":" + image_detail['tag'] foundtypes = [] try: subscription_records = catalog.get_subscription(user_auth, subscription_key=fulltag) for subscription_record in subscription_records: if subscription_record['subscription_key'] == fulltag: foundtypes.append(subscription_record['subscription_type']) except Exception as err: logger.warn("cannot load subscription records - exception: " + str(err)) sub_types = anchore_engine.services.common.subscription_types for sub_type in sub_types: if sub_type in ['repo_update']: continue if sub_type not in foundtypes: try: default_active = False if sub_type in ['tag_update']: default_active = True catalog.add_subscription(user_auth, {'active': default_active, 'subscription_type': sub_type, 'subscription_key': fulltag}) except: try: catalog.update_subscription(user_auth, {'subscription_type': sub_type, 'subscription_key': fulltag}) except: pass # set the state of the image appropriately currstate = image_record['analysis_status'] if not currstate: newstate = taskstate.init_state('analyze', None) elif force or currstate == taskstate.fault_state('analyze'): newstate = taskstate.reset_state('analyze') elif image_record['image_status'] == 'deleted': newstate = taskstate.reset_state('analyze') else: newstate = currstate if (currstate != newstate) or (force): logger.debug("state change detected: " + str(currstate) + " : " + str(newstate)) image_record.update({'image_status': 'active', 'analysis_status': newstate}) updated_image_record = catalog.update_image(user_auth, imageDigest, image_record) if updated_image_record: image_record = updated_image_record[0] else: logger.debug("no state change detected: " + str(currstate) + " : " + str(newstate)) httpcode = 200 return_object = [make_response_image(user_auth, image_record, params)] else: httpcode = 500 raise Exception("failed to add image") except Exception as err: logger.debug("operation exception: " + str(err)) return_object = anchore_engine.services.common.make_response_error(err, in_httpcode=httpcode) httpcode = return_object['httpcode'] return (return_object, httpcode)
def images(request_inputs): user_auth = request_inputs['auth'] method = request_inputs['method'] bodycontent = request_inputs['bodycontent'] params = request_inputs['params'] return_object = {} httpcode = 500 userId, pw = user_auth fulltag = digest = tag = imageId = imageDigest = dockerfile = annotations = None history = False force = False autosubscribe = True query_fulltag = None if params: if 'history' in params: history = params['history'] if 'force' in params: force = params['force'] if 'autosubscribe' in params: autosubscribe = params['autosubscribe'] if 'fulltag' in params: query_fulltag = params['fulltag'] if bodycontent: jsondata = json.loads(bodycontent) if 'digest' in jsondata: digest = jsondata['digest'] if 'tag' in jsondata: tag = jsondata['tag'] #elif 'imageDigest' in jsondata: # imageDigest = jsondata['imageDigest'] #elif 'imageId' in jsondata: # imageId = jsondata['imageId'] if 'dockerfile' in jsondata: dockerfile = jsondata['dockerfile'] if 'annotations' in jsondata: annotations = jsondata['annotations'] autosubscribes = ['analysis_update'] if autosubscribe: autosubscribes.append("tag_update") try: if method == 'GET': logger.debug("handling GET: ") try: return_object = [] # Query param fulltag has precedence for search if query_fulltag: tag = query_fulltag imageId = imageDigest = digest = None image_records = catalog.get_image(user_auth, digest=digest, tag=tag, imageId=imageId, imageDigest=imageDigest, history=history) for image_record in image_records: return_object.append( make_response_image(user_auth, image_record, params)) httpcode = 200 except Exception as err: raise err elif method == 'POST': logger.debug( "handling POST: input_tag={} input_digest={} input_force={}". format(tag, digest, force)) # if not, add it and set it up to be analyzed if not tag: # dont support digest add, yet httpcode = 400 raise Exception("tag is required for image add") if digest and tag: if not force: httpcode = 400 raise Exception("force is required to add digest+tag") else: try: image_check = catalog.get_image(user_auth, digest=digest, tag=tag, imageId=None, imageDigest=digest, history=False) except Exception as err: httpcode = 400 raise Exception( "image digest must already exist to force re-analyze using tag+digest" ) # add the image to the catalog image_record = catalog.add_image(user_auth, tag=tag, digest=digest, dockerfile=dockerfile, annotations=annotations) imageDigest = image_record['imageDigest'] # finally, do any state updates and return if image_record: logger.debug("added image: " + str(imageDigest)) # auto-subscribe for NOW for image_detail in image_record['image_detail']: fulltag = image_detail['registry'] + "/" + image_detail[ 'repo'] + ":" + image_detail['tag'] foundtypes = [] try: subscription_records = catalog.get_subscription( user_auth, subscription_key=fulltag) except Exception as err: subscription_records = [] for subscription_record in subscription_records: if subscription_record['subscription_key'] == fulltag: foundtypes.append( subscription_record['subscription_type']) sub_types = anchore_engine.services.common.subscription_types for sub_type in sub_types: if sub_type in ['repo_update']: continue if sub_type not in foundtypes: try: default_active = False if sub_type in autosubscribes: logger.debug("auto-subscribing image: " + str(sub_type)) default_active = True catalog.add_subscription( user_auth, { 'active': default_active, 'subscription_type': sub_type, 'subscription_key': fulltag }) except: try: catalog.update_subscription( user_auth, { 'subscription_type': sub_type, 'subscription_key': fulltag }) except: pass # set the state of the image appropriately currstate = image_record['analysis_status'] if not currstate: newstate = taskstate.init_state('analyze', None) elif force or currstate == taskstate.fault_state('analyze'): newstate = taskstate.reset_state('analyze') elif image_record['image_status'] == 'deleted': newstate = taskstate.reset_state('analyze') else: newstate = currstate if (currstate != newstate) or (force): logger.debug("state change detected: " + str(currstate) + " : " + str(newstate)) image_record.update({ 'image_status': 'active', 'analysis_status': newstate }) updated_image_record = catalog.update_image( user_auth, imageDigest, image_record) if updated_image_record: image_record = updated_image_record[0] else: logger.debug("no state change detected: " + str(currstate) + " : " + str(newstate)) httpcode = 200 return_object = [ make_response_image(user_auth, image_record, params) ] else: httpcode = 500 raise Exception("failed to add image") except Exception as err: logger.debug("operation exception: " + str(err)) return_object = anchore_engine.services.common.make_response_error( err, in_httpcode=httpcode) httpcode = return_object['httpcode'] return (return_object, httpcode)