Add docker_build for building Docker image using Bazel

docker_build is a Skylark rule that describe a docker image. You can
replace Dockerfile by a docker_build to use Bazel's incrementality model.

--
MOS_MIGRATED_REVID=99160762
diff --git a/tools/build_defs/docker/rewrite_json_test.py b/tools/build_defs/docker/rewrite_json_test.py
new file mode 100644
index 0000000..8bb711d
--- /dev/null
+++ b/tools/build_defs/docker/rewrite_json_test.py
@@ -0,0 +1,621 @@
+# Copyright 2015 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Testing for rewrite_json."""
+
+import unittest
+
+from tools.build_defs.docker.rewrite_json import _DOCKER_VERSION
+from tools.build_defs.docker.rewrite_json import _OPERATING_SYSTEM
+from tools.build_defs.docker.rewrite_json import _PROCESSOR_ARCHITECTURE
+from tools.build_defs.docker.rewrite_json import MetadataOptions
+from tools.build_defs.docker.rewrite_json import RewriteMetadata
+
+
+class RewriteJsonTest(unittest.TestCase):
+  """Testing for rewrite_json."""
+
+  def testNewEntrypoint(self):
+    in_data = {
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor'
+        }
+    }
+    name = 'deadbeef'
+    parent = 'blah'
+    entrypoint = ['/bin/bash']
+    expected = {
+        'id': name,
+        'parent': parent,
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor',
+            'Entrypoint': entrypoint
+        },
+        'docker_version': _DOCKER_VERSION,
+        'architecture': _PROCESSOR_ARCHITECTURE,
+        'os': _OPERATING_SYSTEM,
+    }
+
+    actual = RewriteMetadata(in_data, MetadataOptions(
+        name=name, entrypoint=entrypoint, parent=parent))
+    self.assertEquals(expected, actual)
+
+  def testOverrideEntrypoint(self):
+    in_data = {
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor',
+            'Entrypoint': ['/bin/sh', 'does', 'not', 'matter'],
+        }
+    }
+    name = 'deadbeef'
+    parent = 'blah'
+    entrypoint = ['/bin/bash']
+    expected = {
+        'id': name,
+        'parent': parent,
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor',
+            'Entrypoint': entrypoint
+        },
+        'docker_version': _DOCKER_VERSION,
+        'architecture': _PROCESSOR_ARCHITECTURE,
+        'os': _OPERATING_SYSTEM,
+    }
+
+    actual = RewriteMetadata(in_data, MetadataOptions(
+        name=name, entrypoint=entrypoint, parent=parent))
+    self.assertEquals(expected, actual)
+
+  def testNewCmd(self):
+    in_data = {
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor',
+            'Entrypoint': ['/bin/bash'],
+        }
+    }
+    name = 'deadbeef'
+    parent = 'blah'
+    cmd = ['/bin/bash']
+    expected = {
+        'id': name,
+        'parent': parent,
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor',
+            'Entrypoint': ['/bin/bash'],
+            'Cmd': cmd
+        },
+        'docker_version': _DOCKER_VERSION,
+        'architecture': _PROCESSOR_ARCHITECTURE,
+        'os': _OPERATING_SYSTEM,
+    }
+
+    actual = RewriteMetadata(in_data, MetadataOptions(
+        name=name, cmd=cmd, parent=parent))
+    self.assertEquals(expected, actual)
+
+  def testOverrideCmd(self):
+    in_data = {
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor',
+            'Entrypoint': ['/bin/bash'],
+            'Cmd': ['does', 'not', 'matter'],
+        }
+    }
+    name = 'deadbeef'
+    parent = 'blah'
+    cmd = ['does', 'matter']
+    expected = {
+        'id': name,
+        'parent': parent,
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor',
+            'Entrypoint': ['/bin/bash'],
+            'Cmd': cmd
+        },
+        'docker_version': _DOCKER_VERSION,
+        'architecture': _PROCESSOR_ARCHITECTURE,
+        'os': _OPERATING_SYSTEM,
+    }
+
+    actual = RewriteMetadata(in_data, MetadataOptions(
+        name=name, cmd=cmd, parent=parent))
+    self.assertEquals(expected, actual)
+
+  def testOverrideBoth(self):
+    in_data = {
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor',
+            'Entrypoint': ['/bin/sh'],
+            'Cmd': ['does', 'not', 'matter'],
+        }
+    }
+    name = 'deadbeef'
+    parent = 'blah'
+    entrypoint = ['/bin/bash', '-c']
+    cmd = ['my-command', 'my-arg1', 'my-arg2']
+    expected = {
+        'id': name,
+        'parent': parent,
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor',
+            'Entrypoint': entrypoint,
+            'Cmd': cmd
+        },
+        'docker_version': _DOCKER_VERSION,
+        'architecture': _PROCESSOR_ARCHITECTURE,
+        'os': _OPERATING_SYSTEM,
+    }
+
+    actual = RewriteMetadata(in_data, MetadataOptions(
+        name=name, entrypoint=entrypoint, cmd=cmd, parent=parent))
+    self.assertEquals(expected, actual)
+
+  def testOverrideParent(self):
+    name = 'me!'
+    parent = 'parent'
+    # In the typical case, we expect the parent to
+    # come in as the 'id', and our grandparent to
+    # be its 'parent'.
+    in_data = {
+        'id': parent,
+        'parent': 'grandparent',
+    }
+    expected = {
+        'id': name,
+        'parent': parent,
+        'config': {},
+        'docker_version': _DOCKER_VERSION,
+        'architecture': _PROCESSOR_ARCHITECTURE,
+        'os': _OPERATING_SYSTEM,
+    }
+
+    actual = RewriteMetadata(in_data, MetadataOptions(
+        name=name, parent=parent))
+    self.assertEquals(expected, actual)
+
+  def testNewSize(self):
+    # Size is one of the few fields that, when omitted,
+    # should be removed.
+    in_data = {
+        'id': 'you',
+        'Size': '124',
+    }
+    name = 'me'
+    parent = 'blah'
+    size = '4321'
+    expected = {
+        'id': name,
+        'parent': parent,
+        'Size': size,
+        'config': {},
+        'docker_version': _DOCKER_VERSION,
+        'architecture': _PROCESSOR_ARCHITECTURE,
+        'os': _OPERATING_SYSTEM,
+    }
+
+    actual = RewriteMetadata(in_data, MetadataOptions(
+        name=name, size=size, parent=parent))
+    self.assertEquals(expected, actual)
+
+  def testOmitSize(self):
+    # Size is one of the few fields that, when omitted,
+    # should be removed.
+    in_data = {
+        'id': 'you',
+        'Size': '124',
+    }
+    name = 'me'
+    parent = 'blah'
+    expected = {
+        'id': name,
+        'parent': parent,
+        'config': {},
+        'docker_version': _DOCKER_VERSION,
+        'architecture': _PROCESSOR_ARCHITECTURE,
+        'os': _OPERATING_SYSTEM,
+    }
+
+    actual = RewriteMetadata(in_data, MetadataOptions(
+        name=name, parent=parent))
+    self.assertEquals(expected, actual)
+
+  def testOmitName(self):
+    # Name is required.
+    with self.assertRaises(Exception):
+      RewriteMetadata({}, MetadataOptions(name=None))
+
+  def testStripContainerConfig(self):
+    # Size is one of the few fields that, when omitted,
+    # should be removed.
+    in_data = {
+        'id': 'you',
+        'container_config': {},
+    }
+    name = 'me'
+    parent = 'blah'
+    expected = {
+        'id': name,
+        'parent': parent,
+        'config': {},
+        'docker_version': _DOCKER_VERSION,
+        'architecture': _PROCESSOR_ARCHITECTURE,
+        'os': _OPERATING_SYSTEM,
+    }
+
+    actual = RewriteMetadata(in_data, MetadataOptions(
+        name=name, parent=parent))
+    self.assertEquals(expected, actual)
+
+  def testEmptyBase(self):
+    in_data = {}
+    name = 'deadbeef'
+    entrypoint = ['/bin/bash', '-c']
+    cmd = ['my-command', 'my-arg1', 'my-arg2']
+    size = '999'
+    expected = {
+        'id': name,
+        'config': {
+            'Entrypoint': entrypoint,
+            'Cmd': cmd,
+            'ExposedPorts': {
+                '80/tcp': {}
+            }
+        },
+        'docker_version': _DOCKER_VERSION,
+        'architecture': _PROCESSOR_ARCHITECTURE,
+        'os': _OPERATING_SYSTEM,
+        'Size': size,
+    }
+
+    actual = RewriteMetadata(in_data, MetadataOptions(
+        name=name, entrypoint=entrypoint, cmd=cmd, size=size,
+        ports=['80']))
+    self.assertEquals(expected, actual)
+
+  def testOmitParentWithBase(self):
+    # Our input data should be empty when parent is omitted
+    in_data = {
+        'id': 'you',
+    }
+    with self.assertRaises(Exception):
+      RewriteMetadata(in_data, MetadataOptions(name='me'))
+
+  def testNewPort(self):
+    in_data = {
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor'
+        }
+    }
+    name = 'deadbeef'
+    parent = 'blah'
+    port = '80'
+    expected = {
+        'id': name,
+        'parent': parent,
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor',
+            'ExposedPorts': {
+                port + '/tcp': {}
+            }
+        },
+        'docker_version': _DOCKER_VERSION,
+        'architecture': _PROCESSOR_ARCHITECTURE,
+        'os': _OPERATING_SYSTEM,
+    }
+
+    actual = RewriteMetadata(in_data, MetadataOptions(
+        name=name, parent=parent, ports=[port]))
+    self.assertEquals(expected, actual)
+
+  def testAugmentPort(self):
+    in_data = {
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor',
+            'ExposedPorts': {
+                '443/tcp': {}
+            }
+        }
+    }
+    name = 'deadbeef'
+    parent = 'blah'
+    port = '80'
+    expected = {
+        'id': name,
+        'parent': parent,
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor',
+            'ExposedPorts': {
+                '443/tcp': {},
+                port + '/tcp': {}
+            }
+        },
+        'docker_version': _DOCKER_VERSION,
+        'architecture': _PROCESSOR_ARCHITECTURE,
+        'os': _OPERATING_SYSTEM,
+    }
+
+    actual = RewriteMetadata(in_data, MetadataOptions(
+        name=name, parent=parent, ports=[port]))
+    self.assertEquals(expected, actual)
+
+  def testMultiplePorts(self):
+    in_data = {
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor'
+        }
+    }
+    name = 'deadbeef'
+    parent = 'blah'
+    port1 = '80'
+    port2 = '8080'
+    expected = {
+        'id': name,
+        'parent': parent,
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor',
+            'ExposedPorts': {
+                port1 + '/tcp': {},
+                port2 + '/tcp': {}
+            }
+        },
+        'docker_version': _DOCKER_VERSION,
+        'architecture': _PROCESSOR_ARCHITECTURE,
+        'os': _OPERATING_SYSTEM,
+    }
+
+    actual = RewriteMetadata(in_data, MetadataOptions(
+        name=name, parent=parent, ports=[port1, port2]))
+    self.assertEquals(expected, actual)
+
+  def testPortCollision(self):
+    port = '80'
+    in_data = {
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor',
+            'ExposedPorts': {
+                port + '/tcp': {}
+            }
+        }
+    }
+    name = 'deadbeef'
+    parent = 'blah'
+    expected = {
+        'id': name,
+        'parent': parent,
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor',
+            'ExposedPorts': {
+                port + '/tcp': {}
+            }
+        },
+        'docker_version': _DOCKER_VERSION,
+        'architecture': _PROCESSOR_ARCHITECTURE,
+        'os': _OPERATING_SYSTEM,
+    }
+
+    actual = RewriteMetadata(in_data, MetadataOptions(
+        name=name, parent=parent, ports=[port]))
+    self.assertEquals(expected, actual)
+
+  def testPortWithProtocol(self):
+    in_data = {
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor'
+        }
+    }
+    name = 'deadbeef'
+    parent = 'blah'
+    port = '80/tcp'
+    expected = {
+        'id': name,
+        'parent': parent,
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor',
+            'ExposedPorts': {
+                port: {}
+            }
+        },
+        'docker_version': _DOCKER_VERSION,
+        'architecture': _PROCESSOR_ARCHITECTURE,
+        'os': _OPERATING_SYSTEM,
+    }
+
+    actual = RewriteMetadata(in_data, MetadataOptions(
+        name=name, parent=parent, ports=[port]))
+    self.assertEquals(expected, actual)
+
+  def testNewVolume(self):
+    in_data = {
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor'
+        }
+    }
+    name = 'deadbeef'
+    parent = 'blah'
+    volume = '/logs'
+    expected = {
+        'id': name,
+        'parent': parent,
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor',
+            'Volumes': {
+                volume: {}
+            }
+        },
+        'docker_version': _DOCKER_VERSION,
+        'architecture': _PROCESSOR_ARCHITECTURE,
+        'os': _OPERATING_SYSTEM,
+    }
+
+    actual = RewriteMetadata(in_data, MetadataOptions(
+        name=name, parent=parent, volumes=[volume]))
+    self.assertEquals(expected, actual)
+
+  def testAugmentVolume(self):
+    in_data = {
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor',
+            'Volumes': {
+                '/original': {}
+            }
+        }
+    }
+    name = 'deadbeef'
+    parent = 'blah'
+    volume = '/data'
+    expected = {
+        'id': name,
+        'parent': parent,
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor',
+            'Volumes': {
+                '/original': {},
+                volume: {}
+            }
+        },
+        'docker_version': _DOCKER_VERSION,
+        'architecture': _PROCESSOR_ARCHITECTURE,
+        'os': _OPERATING_SYSTEM,
+    }
+
+    actual = RewriteMetadata(in_data, MetadataOptions(
+        name=name, parent=parent, volumes=[volume]))
+    self.assertEquals(expected, actual)
+
+  def testMultipleVolumes(self):
+    in_data = {
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor'
+        }
+    }
+    name = 'deadbeef'
+    parent = 'blah'
+    volume1 = '/input'
+    volume2 = '/output'
+    expected = {
+        'id': name,
+        'parent': parent,
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor',
+            'Volumes': {
+                volume1: {},
+                volume2: {}
+            }
+        },
+        'docker_version': _DOCKER_VERSION,
+        'architecture': _PROCESSOR_ARCHITECTURE,
+        'os': _OPERATING_SYSTEM,
+    }
+
+    actual = RewriteMetadata(in_data, MetadataOptions(
+        name=name, parent=parent, volumes=[volume1, volume2]))
+    self.assertEquals(expected, actual)
+
+  def testEnv(self):
+    in_data = {
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor'
+        }
+    }
+    name = 'deadbeef'
+    parent = 'blah'
+    env = [
+        'baz=blah',
+        'foo=bar',
+    ]
+    expected = {
+        'id': name,
+        'parent': parent,
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor',
+            'Env': env,
+        },
+        'docker_version': _DOCKER_VERSION,
+        'architecture': _PROCESSOR_ARCHITECTURE,
+        'os': _OPERATING_SYSTEM,
+    }
+
+    actual = RewriteMetadata(in_data, MetadataOptions(
+        name=name, env=env, parent=parent))
+    self.assertEquals(expected, actual)
+
+  def testEnvResolveReplace(self):
+    in_data = {
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor',
+            'Env': [
+                'foo=bar',
+                'baz=blah',
+                'blah=still around',
+            ],
+        }
+    }
+    name = 'deadbeef'
+    parent = 'blah'
+    env = [
+        'baz=replacement',
+        'foo=$foo:asdf',
+    ]
+    expected = {
+        'id': name,
+        'parent': parent,
+        'config': {
+            'User': 'mattmoor',
+            'WorkingDir': '/usr/home/mattmoor',
+            'Env': [
+                'baz=replacement',
+                'blah=still around',
+                'foo=bar:asdf',
+            ],
+        },
+        'docker_version': _DOCKER_VERSION,
+        'architecture': _PROCESSOR_ARCHITECTURE,
+        'os': _OPERATING_SYSTEM,
+    }
+
+    actual = RewriteMetadata(in_data, MetadataOptions(
+        name=name, env=env, parent=parent))
+    self.assertEquals(expected, actual)
+
+
+if __name__ == '__main__':
+  unittest.main()