Skip to content
This repository was archived by the owner on Oct 10, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 125 additions & 12 deletions Atomic/atomic.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ def __init__(self):
self.inspect = None
self.force = False
self._images = []
self.containers = False
self.images = False

def writeOut(self, output, lf="\n"):
sys.stdout.flush()
Expand All @@ -116,7 +118,7 @@ def force_delete_containers(self):
image = self.image
if self.image.find(":") == -1:
image += ":latest"
for c in self.d.containers(all=True):
for c in self.get_containers():
if c["Image"] == image:
self.d.remove_container(c["Id"], force=True)

Expand Down Expand Up @@ -433,6 +435,8 @@ def scan(self):
self.ping()
BUS_NAME = "org.OpenSCAP.daemon"
OBJECT_PATH = "/OpenSCAP/daemon"
INTERFACE = "org.OpenSCAP.daemon.Interface"

if self.args.images:
scan_list = self._get_all_image_ids()
elif self.args.containers:
Expand All @@ -442,13 +446,14 @@ def scan(self):
iids = self._get_all_image_ids()
scan_list = cids + iids
else:
scan_list = self.args.scan_targets

scan_list = []
for scan_input in self.args.scan_targets:
scan_list.append(self.get_input_id(scan_input))
util.writeOut("\nScanning...\n")
bus = dbus.SystemBus()
try:
oscap_d = bus.get_object(BUS_NAME, OBJECT_PATH)
oscap_i = dbus.Interface(oscap_d, "org.OpenSCAP.daemon.Interface")
oscap_i = dbus.Interface(oscap_d, INTERFACE)
scan_return = json.loads(oscap_i.scan_list(scan_list, 4))
except dbus.exceptions.DBusException:
error = "Unable to find the openscap-daemon dbus service."\
Expand Down Expand Up @@ -655,7 +660,7 @@ def images(self):
("REPOSITORY", "TAG", "IMAGE ID", "CREATED",
"VIRTUAL SIZE"))

for image in self.d.images():
for image in self.get_images():
repo, tag = image["RepoTags"][0].split(":")
self.writeOut(
"%s%-35s %-19s %.12s %-19s %-12s" %
Expand Down Expand Up @@ -724,17 +729,17 @@ def get_layers(self):

def _get_all_image_ids(self):
iids = []
for image in self.d.images():
for image in self.get_images():
iids.append(image['Id'])
return iids

def _get_all_container_ids(self):
cids = []
for con in self.d.containers(all=True):
for con in self.get_containers():
cids.append(con['Id'])
return cids

def _get_image(self, image):
def _get_image_infos(self, image):
def get_label(label):
return self.get_label(label, image["Id"])

Expand All @@ -744,13 +749,13 @@ def get_label(label):
get_label("Release"))).strip(":"),
"Tag": image["RepoTags"][0]}

def get_images(self):
def get_image_infos(self):
if len(self._images) > 0:
return self._images

images = self.d.images()
images = self.get_images()
for image in images:
self._images.append(self._get_image(image))
self._images.append(self._get_image_infos(image))

return self._images

Expand All @@ -776,7 +781,7 @@ def get_label(label):
continue
name = layer["Name"]
if len(name) > 0:
for i in self.get_images():
for i in self.get_image_infos():
if i["Name"] == name:
if i["Version"] > layer["Version"]:
buf = ("Image '%s' contains a layer '%s' that is "
Expand Down Expand Up @@ -866,6 +871,114 @@ def ping(self):
sys.stderr.write("\nUnable to communicate with docker daemon\n")
sys.exit(1)

def _is_container(self, identifier):
'''
Checks is the identifier is a container ID or container name. If
it is, returns the full container ID. Else it will return an
AtomicError
'''
err_append = "Refine your search to narrow results."
cons = self.get_containers()
cids = [x['Id'] for x in cons]
con_index = [i for i, j in enumerate(cids) if j.startswith(identifier)]

if len(con_index) > 0:
if len(con_index) > 1:
CIDS = []
for index in con_index:
CIDS.append(cids[index])
raise ValueError("Found multiple container IDs ({0}) that "
" might match '{1}'. {2}"
.format(" ".join(CIDS), identifier,
err_append))
return cids[con_index[0]]

for con in cons:
if "/{0}".format(identifier) in con['Names']:
return con['Id']

# No dice
raise AtomicError

def _is_image(self, identifier):
'''
Checks is the identifier is a image ID or a matches an image name.
If it finds a match, it returns the full image ID. Else it will
return an AtomicError.
'''
err_append = "Refine your search to narrow results."
image_info = self.get_images()
iids = [x['Id'] for x in image_info]
image_index = [i for i, j in enumerate(iids)
if j.startswith(identifier)]

if len(image_index) > 0:
if len(image_index) > 1:
IDS = []
for index in image_index:
IDS.append(iids[index])
raise ValueError("Found multiple image IDs ({0}) that might "
"match '{1}'. {2}".format(" ".join(IDS),
identifier,
err_append))
return iids[image_index[0]]

name_search = util.image_by_name(identifier, images=image_info)
if len(name_search) > 0:
if len(name_search) > 1:
tmp_image = dict((x['Id'], x['RepoTags']) for x in image_info)
repo_tags = []
for name in name_search:
for repo_tag in tmp_image.get(name['Id']):
if repo_tag.find(identifier) > -1:
repo_tags.append(repo_tag)
raise ValueError("Found more than one image possibly "
"matching '{0}'. They are:\n {1} \n{2}"
.format(identifier, "\n ".join(repo_tags),
err_append))
return name_search[0]['Id']

# No dice
raise AtomicError

def get_input_id(self, identifier):
'''
Determine if the input "identifier" is valid. Return the container or
image ID when true and raise a ValueError when not
'''
try:
return self._is_image(identifier)
except AtomicError:
pass
try:
return self._is_container(identifier)
except AtomicError:
pass
raise ValueError("Unable to associate '{0}' with a container or image."
.format(identifier))

def get_images(self):
'''
Wrapper function that should be used instead of querying docker
multiple times for a list of images.
'''
if not self.images:
self.images = self.d.images()
return self.images

def get_containers(self):
'''
Wrapper function that should be used instead of querying docker
multiple times for a list of containers
'''
if not self.containers:
self.containers = self.d.containers(all=True)
return self.containers


class AtomicError(Exception):
pass


def SetFunc(function):
class customAction(argparse.Action):
Expand Down
14 changes: 9 additions & 5 deletions Atomic/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
input = input


def image_by_name(img_name):
def image_by_name(img_name, images=None):
"""
Returns a list of image data for images which match img_name.
Returns a list of image data for images which match img_name. Will
optionally take a list of images from a docker.Client.images
query to avoid multiple docker queries.
"""
def _decompose(compound_name):
""" '[reg/]repo[:tag]' -> (reg, repo, tag) """
Expand All @@ -30,16 +32,18 @@ def _decompose(compound_name):
repo, tag = repo.rsplit(':', 1)
return reg, repo, tag

c = docker.Client()

i_reg, i_rep, i_tag = _decompose(img_name)
# Correct for bash-style matching expressions.
if not i_reg:
i_reg = '*'
if not i_tag:
i_tag = '*'

images = c.images(all=False)
# If the images were not passed in, go get them.
if images is None:
c = docker.Client()
images = c.images(all=False)

valid_images = []
for i in images:
for t in i['RepoTags']:
Expand Down