def img_metadata(self, name, tag, deps):
     img = ImageFSM(os.path.join(fixtures_dir, "foo-bar"),
                    self.builder.client, self.builder.config)
     img.image.short_name = name
     # Clean up the images registry before initiating images this way.
     ImageFSM._instances.pop()
     ImageFSM._instances.append(name)
     img.image.tag = tag
     img.image.metadata["depends"] = deps
     img.state = ImageFSM.STATE_TO_BUILD
     return img
 def test_image_state(self, exists, client):
     exists.return_value = True
     ImageFSM._instances = []
     # We set up no registry, thus we can't have a published image.
     img = ImageFSM(os.path.join(fixtures_dir, "foo-bar"), client,
                    {"seed_image": "test"})
     self.assertEqual(img.state, ImageFSM.STATE_BUILT)
     ImageFSM._instances = []
     exists.return_value = False
     img = ImageFSM(os.path.join(fixtures_dir, "foo-bar"), client,
                    {"seed_image": "test"})
     self.assertEqual(img.state, ImageFSM.STATE_TO_BUILD)
 def test_build(self, verify, build, exists):
     # Simple build
     exists.return_value = False
     # The first image builds correctly, the second one doesn't
     build.side_effect = [True, False]
     # Assume verification is successful
     verify.return_value = True
     img0 = ImageFSM(os.path.join(fixtures_dir, "foo-bar"),
                     self.builder.client, self.builder.config)
     img1 = ImageFSM(os.path.join(fixtures_dir, "foobar-server"),
                     self.builder.client, self.builder.config)
     self.builder.all_images = set([img0, img1])
     result = [r for r in self.builder.build()]
     # Check we did not pull the base image.
     self.builder.client.images.pull.assert_not_called()
     # Check we called verify only once, for the image correctly built
     self.assertEqual(verify.call_count, 1)
     # Check the results.
     self.assertEqual("foo-bar:0.0.1", result[0].label)
     self.assertEqual("verified", result[0].state)
     self.builder.client.api.tag.assert_any_call("foo-bar:0.0.1", "foo-bar",
                                                 "latest")
     self.assertEqual("foobar-server:0.0.1~alpha1", result[1].label)
     self.assertEqual("error", result[1].state)
 def test_images_in_state(self):
     img0 = ImageFSM(os.path.join(fixtures_dir, "foo-bar"),
                     self.builder.client, self.builder.config)
     img1 = ImageFSM(os.path.join(fixtures_dir, "foobar-server"),
                     self.builder.client, self.builder.config)
     img0.state = ImageFSM.STATE_BUILT
     img1.state = ImageFSM.STATE_ERROR
     self.builder.all_images = set([img0, img1])
     self.assertEqual([img0],
                      self.builder.images_in_state(ImageFSM.STATE_BUILT))
 def test_pull_images(self):
     img0 = ImageFSM(os.path.join(fixtures_dir, "foo-bar"),
                     self.builder.client, self.builder.config)
     img1 = ImageFSM(os.path.join(fixtures_dir, "foobar-server"),
                     self.builder.client, self.builder.config)
     img0.state = ImageFSM.STATE_BUILT
     img1.state = ImageFSM.STATE_TO_BUILD
     self.builder.all_images = set([img0, img1])
     # img1 is locally built, but not published. No image should be pulled.
     self.builder.pull_dependencies(img1)
     self.builder.client.images.assert_not_called()
     # now if it's published, we should pull it instead
     img0.state = ImageFSM.STATE_PUBLISHED
     self.builder.pull_dependencies(img1)
     self.builder.client.images.pull.assert_called_with(img0.image.image)
    def test_build_pull(self, pull, verify, build):
        self.builder.pull = True
        verify.return_value = True

        def pull_result(img):
            if img.label == "foobar-server:0.0.1~alpha1":
                img.state = ImageFSM.STATE_ERROR

        pull.side_effect = pull_result
        img0 = ImageFSM(os.path.join(fixtures_dir, "foo-bar"),
                        self.builder.client, self.builder.config)
        img1 = ImageFSM(os.path.join(fixtures_dir, "foobar-server"),
                        self.builder.client, self.builder.config)
        img0.state = ImageFSM.STATE_TO_BUILD
        img1.state = ImageFSM.STATE_TO_BUILD
        build.return_value = True
        self.builder.all_images = set([img0, img1])
        result = [r for r in self.builder.build()]
        # Check we also pulled the base image
        self.builder.client.images.pull.assert_called_with("test")
        pull.assert_has_calls([call(img0), call(img1)])
        assert build.call_count == 1
    def test_publish(self, verify):
        self.builder.client.api = MagicMock()
        self.builder.config["username"] = None
        self.builder.config["password"] = None
        self.builder.config["registry"] = "example.org"
        with patch("docker_pkg.builder.ImageFSM._is_published") as mp:
            mp.return_value = False
            img0 = ImageFSM(os.path.join(fixtures_dir, "foo-bar"),
                            self.builder.client, self.builder.config)
            img1 = ImageFSM(
                os.path.join(fixtures_dir, "foobar-server"),
                self.builder.client,
                self.builder.config,
            )
        # One image was already built, the other was verified.
        img0.state = "built"
        img1.state = "verified"
        self.builder.all_images = set([img0, img1])
        # No image gets published if no credentials are set.
        self.assertEqual([], [r for r in self.builder.publish()])
        self.assertEqual(self.builder.client.api.tag.call_count, 0)
        self.assertEqual(verify.call_count, 0)

        # Now with credentials set
        self.builder.config["username"] = "******"
        self.builder.config["password"] = "******"
        result = [r for r in self.builder.publish()]
        self.assertEqual(ImageFSM.STATE_PUBLISHED, result[1].state)
        self.builder.client.api.push.assert_any_call(
            "example.org/foobar-server",
            "0.0.1~alpha1",
            auth_config={
                "username": "******",
                "password": "******"
            },
        )
        # Only one image needed to be verified before publishing.
        self.assertEqual(verify.call_count, 1)
 def setUp(self):
     ImageFSM._instances = []
     dockerfile.TemplateEngine.setup({}, [])
     with patch("docker.from_env") as client:
         self.img = ImageFSM(os.path.join(fixtures_dir, "foo-bar"), client,
                             {"seed_image": "test"})
class TestImageFSM(unittest.TestCase):
    def setUp(self):
        ImageFSM._instances = []
        dockerfile.TemplateEngine.setup({}, [])
        with patch("docker.from_env") as client:
            self.img = ImageFSM(os.path.join(fixtures_dir, "foo-bar"), client,
                                {"seed_image": "test"})

    def test_init(self):
        self.assertIsInstance(self.img.image, image.DockerImage)
        self.assertEqual(self.img.children, set())
        # Reinitializing the same image raises an error
        self.assertRaises(
            RuntimeError,
            ImageFSM,
            os.path.join(fixtures_dir, "foo-bar"),
            MagicMock(),  # here should go a docker client
            {"seed_image": "test"},
        )

    @patch("docker.from_env")
    @patch("docker_pkg.image.DockerImage.exists")
    def test_image_state(self, exists, client):
        exists.return_value = True
        ImageFSM._instances = []
        # We set up no registry, thus we can't have a published image.
        img = ImageFSM(os.path.join(fixtures_dir, "foo-bar"), client,
                       {"seed_image": "test"})
        self.assertEqual(img.state, ImageFSM.STATE_BUILT)
        ImageFSM._instances = []
        exists.return_value = False
        img = ImageFSM(os.path.join(fixtures_dir, "foo-bar"), client,
                       {"seed_image": "test"})
        self.assertEqual(img.state, ImageFSM.STATE_TO_BUILD)

    def test_label(self):
        self.assertEqual(self.img.label, "foo-bar:0.0.1")
        self.img.config["namespace"] = "test"
        self.img.config["registry"] = "example.org"
        self.assertEqual(self.img.label, "example.org/test/foo-bar:0.0.1")

    def test_name(self):
        self.assertEqual(self.img.name, "foo-bar")

    def test_repr(self):
        self.assertEqual(repr(self.img), "ImageFSM(foo-bar:0.0.1, built)")

    @patch("docker_pkg.image.DockerImage.build")
    def test_build(self, build):
        # An already built image doesn't get built again
        self.img.state = ImageFSM.STATE_BUILT
        self.img.build()
        self.assertEqual(build.call_count, 0)
        # Trying to build a published image raises an error
        self.img.state = ImageFSM.STATE_PUBLISHED
        self.assertRaises(ValueError, self.img.build)
        # If the build fails, we end up in an error state
        self.img.state = ImageFSM.STATE_TO_BUILD
        build.return_value = False
        self.img.build()
        self.assertEqual(self.img.state, ImageFSM.STATE_ERROR)
        # Happy path: to build => built
        self.img.state = ImageFSM.STATE_TO_BUILD
        build.return_value = True
        self.img.build()
        self.assertEqual(self.img.state, ImageFSM.STATE_BUILT)

    @patch("docker_pkg.image.DockerImage.verify")
    def test_verify(self, verify):
        self.img.state = ImageFSM.STATE_BUILT
        self.img.verify()
        self.assertEqual(self.img.state, ImageFSM.STATE_VERIFIED)
        self.assertEqual(verify.call_count, 1)

    @patch("docker_pkg.image.DockerImage.verify")
    def test_verify_no_action(self, verify):
        self.img.state = ImageFSM.STATE_VERIFIED
        self.img.verify()
        self.assertEqual(verify.call_count, 0)

    @patch("docker_pkg.image.DockerImage.verify")
    def test_verify_bad_state(self, verify):
        self.img.state = ImageFSM.STATE_TO_BUILD
        self.assertRaises(ValueError, self.img.verify)
        self.assertEqual(verify.call_count, 0)