Skip to content

Commit

Permalink
completing env list and label list changes
Browse files Browse the repository at this point in the history
  • Loading branch information
gdraheim committed Apr 29, 2018
1 parent d9d972d commit 8813ea7
Show file tree
Hide file tree
Showing 3 changed files with 270 additions and 37 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ descriptive (likeSQL).
./docker-copyedit.py FROM image1 INTO image2 -vv \
set shell cmd "/entrypoint.sh foo"
./docker-copyedit.py FROM image1 INTO image2 -vv \
set label author "real me" and rm label oldie
set label author "real me" and rm labels old%
./docker-copyedit.py FROM image1 INTO image2 -vv \
set env MAINDIR "/path" and rm env backupdir

./docker-copyedit.py FROM image1 INTO image2 -vv \
REMOVE PORT 4444
Expand All @@ -66,6 +68,7 @@ You will be left with a dangling old (untagged) image.
Other than 'entrypoint','cmd' and 'user' you can also set
the string values for 'workdir'/'workingdir', 'domainname',
'hostname', 'arch'/'architecture' and 'author' in configs.
The values in the env list and label list can be modified too.
If the edit command did not really change something then
the edited image is not loaded back from disk. Instead the
old image is possibly just tagged with the new name.
Expand Down
171 changes: 168 additions & 3 deletions docker-copyedit-tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from __future__ import print_function

__copyright__ = "(C) 2017-2018 Guido U. Draheim, licensed under the EUPL"
__version__ = "1.0.1176"
__version__ = "1.0.1177"

import subprocess
import collections
Expand Down Expand Up @@ -1441,7 +1441,7 @@ def test_900_change_license_label(self):
#
self.assertEqual(dat1[0]["Config"]["Labels"]["license"], u"free")
self.assertEqual(dat2[0]["Config"]["Labels"]["license"], u"LGPLv2")
def test_910_change_info_label(self):
def test_901_change_info_label(self):
img = IMG
testname = self.testname()
testdir = self.testdir()
Expand Down Expand Up @@ -1479,7 +1479,7 @@ def test_910_change_info_label(self):
#
self.assertEqual(dat1[0]["Config"]["Labels"]["info"], u"free")
self.assertEqual(dat2[0]["Config"]["Labels"]["info"], u"new")
def test_920_remove_other_label(self):
def test_910_remove_other_label(self):
img = IMG
testname = self.testname()
testdir = self.testdir()
Expand Down Expand Up @@ -1518,6 +1518,171 @@ def test_920_remove_other_label(self):
#
self.assertEqual(dat1[0]["Config"]["Labels"]["other"], u"text")
self.assertEqual(dat2[0]["Config"]["Labels"].get("other", "<nonexistant>"), u"<nonexistant>")
def test_920_remove_info_labels(self):
img = IMG
testname = self.testname()
testdir = self.testdir()
text_file(os_path(testdir, "Dockerfile"),"""
FROM centos:centos7
RUN { echo "#! /bin/sh"; echo "exec sleep 4"; } > /entrypoint.sh
RUN chmod 0700 /entrypoint.sh
LABEL info1 free
LABEL other text
LABEL info2 next
LABEL MORE info
CMD ["/entrypoint.sh"]
""")
cmd = "docker build {testdir} -t {img}:{testname}"
run = sh(cmd.format(**locals()))
logg.info("%s\n%s", run.stdout, run.stderr)
#
cmd = "docker inspect {img}:{testname}"
run = sh(cmd.format(**locals()))
data = json.loads(run.stdout)
logg.info("LABELS:\n%s", data[0]["Config"]["Labels"])
logg.info("{testname} Info1 = %s", data[0]["Config"]["Labels"]["info1"])
dat1 = data
#
cmd = "./docker-copyedit.py FROM {img}:{testname} INTO {img}:{testname}x REMOVE LABELS info% -vv"
run = sh(cmd.format(**locals()))
logg.info("%s\n%s\n%s", cmd, run.stdout, run.stderr)
#
cmd = "docker inspect {img}:{testname}x"
run = sh(cmd.format(**locals()))
data = json.loads(run.stdout)
logg.debug("CONFIG:\n%s", data[0]["Config"])
dat2 = data
#
cmd = "docker rmi {img}:{testname} {img}:{testname}x"
run = sh(cmd.format(**locals()))
logg.info("[%s] %s", run.returncode, cmd.format(**locals()))
#
self.assertEqual(dat1[0]["Config"]["Labels"]["info1"], u"free")
self.assertEqual(dat1[0]["Config"]["Labels"]["info2"], u"next")
self.assertEqual(dat2[0]["Config"]["Labels"].get("info1", "<nonexistant>"), u"<nonexistant>")
self.assertEqual(dat2[0]["Config"]["Labels"].get("info2", "<nonexistant>"), u"<nonexistant>")
self.assertEqual(dat2[0]["Config"]["Labels"]["other"], u"text")
self.assertEqual(dat2[0]["Config"]["Labels"]["MORE"], u"info")
def test_950_change_info_env(self):
img = IMG
testname = self.testname()
testdir = self.testdir()
text_file(os_path(testdir, "Dockerfile"),"""
FROM centos:centos7
RUN { echo "#! /bin/sh"; echo "exec sleep 4"; } > /entrypoint.sh
RUN chmod 0700 /entrypoint.sh
ENV INFO free
CMD ["/entrypoint.sh"]
""")
cmd = "docker build {testdir} -t {img}:{testname}"
run = sh(cmd.format(**locals()))
logg.info("%s\n%s", run.stdout, run.stderr)
#
cmd = "docker inspect {img}:{testname}"
run = sh(cmd.format(**locals()))
data = json.loads(run.stdout)
logg.info("Env:\n%s", data[0]["Config"]["Env"])
dat1 = data
#
cmd = "./docker-copyedit.py FROM {img}:{testname} INTO {img}:{testname}x SET ENV INFO new -vv"
run = sh(cmd.format(**locals()))
logg.info("%s\n%s\n%s", cmd, run.stdout, run.stderr)
#
cmd = "docker inspect {img}:{testname}x"
run = sh(cmd.format(**locals()))
data = json.loads(run.stdout)
logg.debug("CONFIG:\n%s", data[0]["Config"])
dat2 = data
#
cmd = "docker rmi {img}:{testname} {img}:{testname}x"
run = sh(cmd.format(**locals()))
logg.info("[%s] %s", run.returncode, cmd.format(**locals()))
#
self.assertIn("INFO=free", dat1[0]["Config"]["Env"])
self.assertIn("INFO=new", dat2[0]["Config"]["Env"])
def test_960_remove_other_env(self):
img = IMG
testname = self.testname()
testdir = self.testdir()
text_file(os_path(testdir, "Dockerfile"),"""
FROM centos:centos7
RUN { echo "#! /bin/sh"; echo "exec sleep 4"; } > /entrypoint.sh
RUN chmod 0700 /entrypoint.sh
ENV INFO free
ENV OTHER text
CMD ["/entrypoint.sh"]
""")
cmd = "docker build {testdir} -t {img}:{testname}"
run = sh(cmd.format(**locals()))
logg.info("%s\n%s", run.stdout, run.stderr)
#
cmd = "docker inspect {img}:{testname}"
run = sh(cmd.format(**locals()))
data = json.loads(run.stdout)
logg.info("Env:\n%s", data[0]["Config"]["Env"])
dat1 = data
#
cmd = "./docker-copyedit.py FROM {img}:{testname} INTO {img}:{testname}x REMOVE ENV OTHER -vv"
run = sh(cmd.format(**locals()))
logg.info("%s\n%s\n%s", cmd, run.stdout, run.stderr)
#
cmd = "docker inspect {img}:{testname}x"
run = sh(cmd.format(**locals()))
data = json.loads(run.stdout)
logg.debug("CONFIG:\n%s", data[0]["Config"])
dat2 = data
#
cmd = "docker rmi {img}:{testname} {img}:{testname}x"
run = sh(cmd.format(**locals()))
logg.info("[%s] %s", run.returncode, cmd.format(**locals()))
#
self.assertIn("INFO=free", dat1[0]["Config"]["Env"])
self.assertNotIn("OTHER=text", dat2[0]["Config"]["Env"])
def test_970_remove_info_envs(self):
img = IMG
testname = self.testname()
testdir = self.testdir()
text_file(os_path(testdir, "Dockerfile"),"""
FROM centos:centos7
RUN { echo "#! /bin/sh"; echo "exec sleep 4"; } > /entrypoint.sh
RUN chmod 0700 /entrypoint.sh
ENV INFO1 free
ENV OTHER text
ENV INFO2 next
ENV MORE info
CMD ["/entrypoint.sh"]
""")
cmd = "docker build {testdir} -t {img}:{testname}"
run = sh(cmd.format(**locals()))
logg.info("%s\n%s", run.stdout, run.stderr)
#
cmd = "docker inspect {img}:{testname}"
run = sh(cmd.format(**locals()))
data = json.loads(run.stdout)
logg.info("Env:\n%s", data[0]["Config"]["Env"])
dat1 = data
#
cmd = "./docker-copyedit.py FROM {img}:{testname} INTO {img}:{testname}x REMOVE ENVS INFO% -vv"
run = sh(cmd.format(**locals()))
logg.info("%s\n%s\n%s", cmd, run.stdout, run.stderr)
#
cmd = "docker inspect {img}:{testname}x"
run = sh(cmd.format(**locals()))
data = json.loads(run.stdout)
logg.debug("CONFIG:\n%s", data[0]["Config"])
dat2 = data
#
cmd = "docker rmi {img}:{testname} {img}:{testname}x"
run = sh(cmd.format(**locals()))
logg.info("[%s] %s", run.returncode, cmd.format(**locals()))
#
self.assertIn("INFO1=free", dat1[0]["Config"]["Env"])
self.assertIn("INFO2=next", dat1[0]["Config"]["Env"])
self.assertNotIn("INFO1=free", dat2[0]["Config"]["Env"])
self.assertNotIn("INFO2=next", dat2[0]["Config"]["Env"])
self.assertIn("OTHER=text", dat1[0]["Config"]["Env"])
self.assertIn("OTHER=text", dat2[0]["Config"]["Env"])
self.assertIn("MORE=info", dat1[0]["Config"]["Env"])

if __name__ == "__main__":
## logging.basicConfig(level = logging.INFO)
Expand Down
131 changes: 98 additions & 33 deletions docker-copyedit.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from __future__ import print_function

__copyright__ = "(C) 2017-2018 Guido U. Draheim, licensed under the EUPL"
__version__ = "1.1.1176"
__version__ = "1.1.1177"

import subprocess
import collections
Expand Down Expand Up @@ -248,34 +248,6 @@ def edit_datadir(datadir, out, edits):
logg.warning("done edit %s %s", action, arg)
except KeyError, e:
logg.warning("there was no '%s' in %s", key, config_filename)
if action in ["remove-label", "rm-label"]:
key = "Labels"
try:
if key in config[CONFIG]:
del config[CONFIG][key][target]
logg.warning("done actual %s %s '%s'", action, target, arg)
except KeyError, e:
logg.warning("there was no label %s in %s", target, config_filename)
if action in ["set-label"]:
key = "Labels"
try:
if arg in ["", "null", "NULL" ]:
value = u''
else:
value = arg
if key not in config[CONFIG]:
config[key] = {}
if target in config[CONFIG][key]:
if config[CONFIG][key][target] == value:
logg.warning("unchanged label '%s' %s", target, value)
else:
config[CONFIG][key][target] = value
logg.warning("done edit label '%s' %s", target, value)
else:
config[CONFIG][key][target] = value
logg.warning("done new label '%s' %s", target, value)
except KeyError, e:
logg.warning("there was no config %s in %s", target, config_filename)
if action in ["set"] and target in StringConfigs:
key = StringConfigs[target]
try:
Expand Down Expand Up @@ -312,6 +284,96 @@ def edit_datadir(datadir, out, edits):
logg.warning("config = %s", config)
except KeyError, e:
logg.warning("there was no meta %s in %s", target, config_filename)
if action in ["set-label"]:
key = "Labels"
try:
value = arg or u''
if key not in config[CONFIG]:
config[key] = {}
if target in config[CONFIG][key]:
if config[CONFIG][key][target] == value:
logg.warning("unchanged label '%s' %s", target, value)
else:
config[CONFIG][key][target] = value
logg.warning("done edit label '%s' %s", target, value)
else:
config[CONFIG][key][target] = value
logg.warning("done new label '%s' %s", target, value)
except KeyError, e:
logg.warning("there was no config %s in %s", target, config_filename)
if action in ["remove-label", "rm-label"]:
key = "Labels"
try:
if key in config[CONFIG]:
del config[CONFIG][key][target]
logg.warning("done actual %s %s ", action, target)
except KeyError, e:
logg.warning("there was no label %s in %s", target, config_filename)
if action in ["remove-labels", "rm-labels"]:
key = "Labels"
try:
pattern = target.replace("%", "*")
args = []
if key in config[CONFIG]:
for entry in config[CONFIG][key]:
if fnmatch(entry, pattern):
args += [ entry ]
for arg in args:
del config[CONFIG][key][arg]
logg.warning("done actual %s %s (%s)", action, target, arg)
except KeyError, e:
logg.warning("there was no label %s in %s", target, config_filename)
if action in ["remove-envs", "rm-envs"]:
key = "Env"
try:
pattern = target.strip() + "=*"
pattern = pattern.replace("%", "*")
found = []
if key in config[CONFIG]:
for n, entry in enumerate(config[CONFIG][key]):
if fnmatch(entry, pattern):
found += [ n ]
for n in reversed(found):
del config[CONFIG][key][n]
logg.warning("done actual %s %s (%s)", action, target, n)
except KeyError, e:
logg.warning("there was no label %s in %s", target, config_filename)
if action in ["remove-env", "rm-env"]:
key = "Env"
try:
pattern = target.strip() + "="
found = []
if key in config[CONFIG]:
for n, entry in enumerate(config[CONFIG][key]):
if entry.startswith(pattern):
found += [ n ]
for n in reversed(found):
del config[CONFIG][key][n]
logg.warning("done actual %s %s (%s)", action, target, n)
except KeyError, e:
logg.warning("there was no label %s in %s", target, config_filename)
if action in ["set-env"]:
key = "Env"
try:
pattern = target.strip() + "="
value = pattern + (arg or u'')
if key not in config[CONFIG]:
config[key] = {}
found = None
for n, entry in enumerate(config[CONFIG][key]):
if entry.startswith(pattern):
found = n
if found is not None:
if config[CONFIG][key][found] == value:
logg.warning("unchanged var '%s' %s", target, value)
else:
config[CONFIG][key][found] = value
logg.warning("done edit var '%s' %s", target, value)
else:
config[CONFIG][key] += [ pattern + value ]
logg.warning("done new var '%s' %s", target, value)
except KeyError, e:
logg.warning("there was no config %s in %s", target, config_filename)
logg.debug("resulting config: %s", config['container_config'])
new_config_text = json.dumps(config)
if new_config_text != old_config_text:
Expand Down Expand Up @@ -371,16 +433,19 @@ def parsing(args):
continue
action = arg.lower()
continue
if action in ["rm-label", "remove-label"]:
rm_labels = ["rm-label", "remove-label", "rm-labels", "remove-labels"]
rm_vars = ["rm-var", "remove-var", "rm-vars", "remove-vars"]
rm_envs = ["rm-env", "remove-env", "rm-envs", "remove-envs"]
if action in (rm_labels + rm_vars + rm_envs):
target = arg
commands.append((action, target, None))
action, target = None, None
continue
#
if action in ["set"] and arg.lower() in ["shell", "label"]:
if action in ["set"] and arg.lower() in ["shell", "label", "labels", "var", "vars", "env", "envs"]:
action = "%s-%s" % (action, arg.lower())
continue
if action in ["rm", "remove"] and arg.lower() in ["label"]:
if action in ["rm", "remove"] and arg.lower() in ["label", "labels", "var", "vars", "env", "envs"]:
action = "%s-%s" % (action, arg.lower())
continue
if action in ["from"]:
Expand Down Expand Up @@ -421,7 +486,7 @@ def parsing(args):
continue
logg.error("unknown edit command starting with %s %s", action, arg)
return None, None, None
elif action in ["set-label"]:
elif action in ["set-label", "set-var", "set-env"]:
target = arg
continue
else:
Expand Down

0 comments on commit 8813ea7

Please sign in to comment.