Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add logic to handle :<port> in key or value for swap map entries #50

Merged
merged 9 commits into from
Sep 2, 2021
82 changes: 55 additions & 27 deletions app/imageswap/imageswap.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,13 +209,32 @@ def build_swap_map(map_file):
# Skip commented lines
if line[0] == "#":
continue
(key, val) = line.split(":")
# Trim trailing comments
if "#" in val:
val_trimmed = re.sub(r"(^.*[^#])(#.*$)", r"\1", val)
maps[key] = re.sub(r" ", "", val_trimmed.rstrip())
if "#" in line:
line = re.sub(r"(^.*[^#])(#.*$)", r"\1", line)
# Trim whitespace
line = re.sub(r" ", "", line.rstrip())
# Check for new style separator ("::") and verify the map splits correctly
if "::" in line and len(line.split("::")) == 2:
(key, val) = line.split("::")
# Check for old style separator (":") and verify the map splits correctly
elif ":" in line and len(line.split(":")) == 2:
app.logger.warning(
f'Map defined with ":" as separator. This syntax is now deprecated. Please use "::" to separate the key and value in the map file: {line}'
)
(key, val) = line.split(":")
else:
maps[key] = re.sub(r" ", "", val.rstrip())
# Check if map key contains a ":port" and that the new style separator ("::") is not used
if line.count(":") > 1 and "::" not in line:
app.logger.warning(
f'Invalid map is specified. A port in the map key or value requires using "::" as the separator. Skipping map for line: {line}'
)
# Warn for any other invalid map syntax
else:
app.logger.warning(f"Invalid map is specified. Incorrect syntax for map definition. Skipping map for line: {line}")
continue
# Store processed line key/value pair in map
maps[key] = val

f.close()

Expand Down Expand Up @@ -246,11 +265,9 @@ def swap_image(container_spec):
# Set docker.io if no registry is detected
image_registry = "docker.io"
no_registry = True
# Check if Registry portion includes a ":<port_number>"
if ":" in image_registry:
image_registry_noport = image_registry.partition(":")[0]
else:
image_registry_noport = image_registry

# Set the image registry key to work with
image_registry_key = image_registry

# Check the imageswap mode
if imageswap_mode.lower() == "maps":
Expand All @@ -259,6 +276,17 @@ def swap_image(container_spec):

swap_maps = build_swap_map(imageswap_maps_file)

app.logger.debug(f"Swap Maps:\n{swap_maps}")

# Check if Registry portion includes a ":<port_number>"
if ":" in image_registry:
image_registry_noport = image_registry.partition(":")[0]
else:
image_registry_noport = image_registry

if image_registry not in swap_maps and image_registry_noport in swap_maps:
image_registry_key = image_registry_noport

# Verify the default map exists or skip swap
if imageswap_maps_default_key not in swap_maps:
app.logger.warning(f'You don\'t have a "{imageswap_maps_default_key}" entry in your ImageSwap Map config, skipping swap')
Expand All @@ -268,8 +296,8 @@ def swap_image(container_spec):
if imageswap_maps_wildcard_key in swap_maps and swap_maps[imageswap_maps_wildcard_key] != "":
wildcard_maps = str(swap_maps[imageswap_maps_wildcard_key]).split(",")

# Check if bare registry/registry+library has a map specified
if image_registry_noport in swap_maps or image_registry_noport + "/library" in swap_maps:
# Check if registry or registry+library has a map specified
if image_registry_key in swap_maps or image_registry_key + "/library" in swap_maps:

# Check for Library image (ie. empty strings for index 1 an 2 in image_split)
if image_split[1] == "" and image_split[2] == "":
Expand All @@ -278,30 +306,30 @@ def swap_image(container_spec):
else:
app.logger.debug("Image is not a Library image")

if library_image and image_registry_noport + "/library" in swap_maps:
if library_image and image_registry_key + "/library" in swap_maps:

image_registry_noport = image_registry_noport + "/library"
app.logger.info(f"Library Image detected and matching Map found: {image_registry_noport}")
image_registry_key = image_registry_key + "/library"
app.logger.info(f"Library Image detected and matching Map found: {image_registry_key}")
app.logger.debug("More info on Library Image: https://docs.docker.com/registry/introduction/#understanding-image-naming")

# If the swap map has no value, swapping should be skipped
if swap_maps[image_registry_noport] == "":
app.logger.debug(f'Swap map for "{image_registry_noport}" has no value assigned, skipping swap')
if swap_maps[image_registry_key] == "":
app.logger.debug(f'Swap map for "{image_registry_key}" has no value assigned, skipping swap')
return False
# If the image prefix ends with "-" just append existing image (minus any ":<port_number>")
elif swap_maps[image_registry_noport][-1] == "-":
elif swap_maps[image_registry_key][-1] == "-":
if no_registry:
new_image = swap_maps[image_registry_noport] + image_registry_noport + "/" + re.sub(r":.*/", "/", image)
new_image = swap_maps[image_registry_key] + image_registry_noport + "/" + re.sub(r":.*/", "/", image)
else:
new_image = swap_maps[image_registry_noport] + re.sub(r":.*/", "/", image)
# If the image registry without a port pattern is found in the original image
elif image_registry_noport in image:
new_image = re.sub(image_registry_noport, swap_maps[image_registry_noport], image)
new_image = swap_maps[image_registry_key] + re.sub(r":.*/", "/", image)
# If the image registry pattern is found in the original image
elif image_registry_key in image:
new_image = re.sub(image_registry_key, swap_maps[image_registry_key], image)
# For everything else
else:
new_image = swap_maps[image_registry_noport] + "/" + image
new_image = swap_maps[image_registry_key] + "/" + image

app.logger.debug(f'Swap Map = "{image_registry_noport}" : "{swap_maps[image_registry_noport]}"')
app.logger.debug(f'Swap Map = "{image_registry_key}" : "{swap_maps[image_registry_key]}"')

# Check if any of the noswap wildcard patterns from the swap map exist within the original image
elif len(wildcard_maps) > 0 and any(noswap in image for noswap in wildcard_maps):
Expand All @@ -311,15 +339,15 @@ def swap_image(container_spec):
# Using Default image swap map
else:

app.logger.debug(f'No Swap map for "{image_registry_noport}" detected, using default map')
app.logger.debug(f'No Swap map for "{image_registry_key}" detected, using default map')
app.logger.debug(f'Swap Map = "default" : "{swap_maps[imageswap_maps_default_key]}"')

if swap_maps[imageswap_maps_default_key] == "":
app.logger.debug(f"Default map has no value assigned, skipping swap")
return False
elif swap_maps[imageswap_maps_default_key][-1] == "-":
new_image = swap_maps[imageswap_maps_default_key] + image_registry_noport + "/" + image
elif image_registry_noport in image:
elif image_registry_key in image:
new_image = re.sub(image_registry, swap_maps[imageswap_maps_default_key], image)
else:
new_image = swap_maps[imageswap_maps_default_key] + "/" + image
Expand Down
74 changes: 74 additions & 0 deletions app/imageswap/test/test_image_map_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,38 @@ def test_map_config_no_default(self):
self.assertFalse(result)
self.assertEqual(container_spec["image"], expected_image)

def test_map_config_with_port_in_key_and_old_separator(self):

"""Method to test Map File config (map with port in key and old separator ":")"""

imageswap.imageswap_maps_file = "./testing/map_files/map_file.conf"

container_spec = {}
container_spec["name"] = "test-container"
container_spec["image"] = "registry2.bar.com/jmsearcy/twrtools:latest"

expected_image = "default.example.com/jmsearcy/twrtools:latest"
result = imageswap.swap_image(container_spec)

self.assertTrue(result)
self.assertEqual(container_spec["image"], expected_image)

def test_map_config_with_port_in_value_and_old_separator(self):

"""Method to test Map File config (map with port in value and old separator ":")"""

imageswap.imageswap_maps_file = "./testing/map_files/map_file.conf"

container_spec = {}
container_spec["name"] = "test-container"
container_spec["image"] = "registry3.bar.com:8443/jmsearcy/twrtools:latest"

expected_image = "default.example.com/jmsearcy/twrtools:latest"
result = imageswap.swap_image(container_spec)

self.assertTrue(result)
self.assertEqual(container_spec["image"], expected_image)


@patch("imageswap.imageswap_mode", "MAPS")
@patch("imageswap.imageswap_maps_file", "./testing/map_files/map_file.conf")
Expand Down Expand Up @@ -214,6 +246,48 @@ def test_map_config_with_library_image_with_dotted_tag(self):
self.assertTrue(result)
self.assertEqual(container_spec["image"], expected_image)

def test_map_config_with_port_in_key(self):

"""Method to test Map File config (map with port in key)"""

container_spec = {}
container_spec["name"] = "test-container"
container_spec["image"] = "registry.waldo.com:8443/jmsearcy/twrtools:latest"

expected_image = "registry.garply.com/jmsearcy/twrtools:latest"
result = imageswap.swap_image(container_spec)

self.assertTrue(result)
self.assertEqual(container_spec["image"], expected_image)

def test_map_config_with_port_in_value(self):

"""Method to test Map File config (map with port in value)"""

container_spec = {}
container_spec["name"] = "test-container"
container_spec["image"] = "registry.foo.com/jmsearcy/twrtools:latest"

expected_image = "localhost:30003/foo/jmsearcy/twrtools:latest"
result = imageswap.swap_image(container_spec)

self.assertTrue(result)
self.assertEqual(container_spec["image"], expected_image)

def test_map_config_with_port_in_key_and_value(self):

"""Method to test Map File config (map with port in key & value)"""

container_spec = {}
container_spec["name"] = "test-container"
container_spec["image"] = "registry.bar.com:8443/jmsearcy/twrtools:latest"

expected_image = "registry.baz.com:30003/bar/jmsearcy/twrtools:latest"
result = imageswap.swap_image(container_spec)

self.assertTrue(result)
self.assertEqual(container_spec["image"], expected_image)


if __name__ == "__main__":
unittest.main()
6 changes: 6 additions & 0 deletions testing/map_files/map_file.conf
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,10 @@ gitlab.com:registry.example.com/gitlab
cool.io:
registry.internal.twr.io:registry.example.com
harbor.geo.pks.twr.io:harbor2.com ###### This is a comment with many symbols
registry.waldo.com:8443::registry.garply.com # Swap map key that includes a port
registry.foo.com::localhost:30003/foo # Swap map value that includes a port
registry.bar.com:8443::registry.baz.com:30003/bar # Swap map key & value that include a port
registry2.bar.com:registry2.baz.com:30003/bar # Bad config without "::" separator
registry3.bar.com:8443:registry.internal.baz.com:30003/bar # Bad config without "::" separator
registry4.bar.com;registry.internal.baz.com/bar # Bad config with invalid separator
noswap_wildcards:twr.io, walrus.io