Exemple #1
0
    def test_redirect(self):
        test_tree = tree.Tree(self.config)

        # Create some channels and aliases
        test_tree.create_channel("parent")
        test_tree.create_channel_redirect("redirect", "parent")

        # Test standard failure cases
        self.assertRaises(KeyError, test_tree.create_channel_redirect,
                          "redirect", "parent")
        self.assertRaises(KeyError, test_tree.create_channel_redirect,
                          "redirect1", "parent1")
        self.assertRaises(KeyError, test_tree.sync_redirects, "parent1")

        # Publish a basic image
        test_tree.create_device("parent", "device")

        # # First file
        first = os.path.join(self.config.publish_path, "parent/device/full")
        open(first, "w+").close()
        gpg.sign_file(self.config, "image-signing", first)

        # # Second file
        second = os.path.join(self.config.publish_path,
                              "parent/device/version-1234.tar.xz")

        tools.generate_version_tarball(self.config, "parent", "test", "1234",
                                       second.replace(".xz", ""))
        tools.xz_compress(second.replace(".xz", ""))
        os.remove(second.replace(".xz", ""))
        gpg.sign_file(self.config, "image-signing", second)

        with open(second.replace(".tar.xz", ".json"), "w+") as fd:
            metadata = {}
            metadata['channel.ini'] = {}
            metadata['channel.ini']['version_detail'] = "test"
            fd.write(json.dumps(metadata))
        gpg.sign_file(self.config, "image-signing",
                      second.replace(".tar.xz", ".json"))

        # # Adding the entry
        device = test_tree.get_device("parent", "device")
        device.create_image(
            "full", 1234, "abc",
            ["parent/device/full", "parent/device/version-1234.tar.xz"])
        device.set_phased_percentage(1234, 50)

        # # Sync the redirects
        test_tree.sync_redirects("parent")

        # # Get the target
        target = test_tree.get_device("redirect", "device")

        # Confirm the fs layout
        self.assertTrue(not os.path.exists(
            os.path.join(self.config.publish_path, "redirect")))
        self.assertTrue(
            os.path.exists(
                os.path.join(self.config.publish_path, "parent", "device")))
        self.assertEqual(device.list_images(), target.list_images())
Exemple #2
0
    def sync_alias(self, channel_name):
        """
            Update a channel with data from its parent.
        """

        with channels_json(self.config, self.indexpath) as channels:
            if channel_name not in channels:
                raise KeyError("Couldn't find channel: %s" % channel_name)

            if "alias" not in channels[channel_name] or \
                    channels[channel_name]['alias'] == channel_name:
                raise TypeError("Not a channel alias")

            target_name = channels[channel_name]['alias']

            if target_name not in channels:
                raise KeyError("Couldn't find target channel: %s" %
                               target_name)

            # Start by looking for added/removed devices
            devices = set(channels[channel_name]['devices'].keys())
            target_devices = set(channels[target_name]['devices'].keys())

            # # Remove any removed device
            for device in devices - target_devices:
                self.remove_device(channel_name, device)

            # # Add any missing device
            for device in target_devices - devices:
                self.create_device(channel_name, device)

            # Iterate through all the devices to import builds
            for device_name in target_devices:
                device = self.get_device(channel_name, device_name)
                target_device = self.get_device(target_name, device_name)

                # Extract all the current builds
                device_images = {(image['version'], image.get("base", None),
                                  image['type'])
                                 for image in device.list_images()}

                target_images = {(image['version'], image.get("base", None),
                                  image['type'])
                                 for image in target_device.list_images()}

                # Remove any removed image
                for image in device_images - target_images:
                    device.remove_image(image[2], image[0], base=image[1])

                # Create the path if it doesn't exist
                if not os.path.exists(device.path):
                    os.makedirs(device.path)

                # Add any missing image
                with index_json(self.config, device.indexpath, True) as index:
                    for image in sorted(target_images - device_images):
                        orig = [entry for entry in target_device.list_images()
                                if entry['type'] == image[2] and
                                entry['version'] == image[0] and
                                entry.get("base", None) == image[1]]

                        entry = copy.deepcopy(orig[0])

                        # Remove the current version tarball
                        version_detail = None
                        version_index = len(entry['files'])
                        for fentry in entry['files']:
                            if fentry['path'].endswith("version-%s.tar.xz" %
                                                       entry['version']):

                                version_path = "%s/%s" % (
                                    self.config.publish_path, fentry['path'])

                                if os.path.exists(
                                        version_path.replace(".tar.xz",
                                                             ".json")):
                                    with open(
                                            version_path.replace(
                                                ".tar.xz", ".json")) as fd:
                                        metadata = json.loads(fd.read())
                                        if "channel.ini" in metadata:
                                            version_detail = \
                                                metadata['channel.ini'].get(
                                                    "version_detail", None)

                                version_index = fentry['order']
                                entry['files'].remove(fentry)
                                break

                        # Generate a new one
                        path = os.path.join(device.path,
                                            "version-%s.tar.xz" %
                                            entry['version'])
                        abspath, relpath = tools.expand_path(path,
                                                             device.pub_path)
                        if not os.path.exists(abspath):
                            tools.generate_version_tarball(
                                self.config, channel_name, device_name,
                                str(entry['version']),
                                abspath.replace(".xz", ""),
                                version_detail=version_detail,
                                channel_target=target_name)
                            tools.xz_compress(abspath.replace(".xz", ""))
                            os.remove(abspath.replace(".xz", ""))
                            gpg.sign_file(self.config, "image-signing",
                                          abspath)

                        with open(abspath, "rb") as fd:
                            checksum = sha256(fd.read()).hexdigest()

                        # Generate the new file entry
                        version = {}
                        version['order'] = version_index
                        version['path'] = "/%s" % "/".join(
                            relpath.split(os.sep))
                        version['signature'] = "/%s.asc" % "/".join(
                            relpath.split(os.sep))
                        version['checksum'] = checksum
                        version['size'] = int(os.stat(abspath).st_size)

                        # And add it
                        entry['files'].append(version)
                        index['images'].append(entry)

                # Sync phased-percentage
                versions = sorted({entry[0] for entry in target_images})
                if versions:
                    device.set_phased_percentage(
                        versions[-1],
                        target_device.get_phased_percentage(versions[-1]))

        return True
Exemple #3
0
    def test_rename(self):
        test_tree = tree.Tree(self.config)

        # Create some channels and aliases
        test_tree.create_channel("old")
        test_tree.create_channel("existing")

        # Test standard failure cases
        self.assertRaises(KeyError, test_tree.rename_channel, "old1",
                          "test/new")
        self.assertRaises(KeyError, test_tree.rename_channel, "old",
                          "existing")

        # Publish a basic image
        test_tree.create_device("old", "device")

        # # First file
        first = os.path.join(self.config.publish_path, "old/device/full")
        open(first, "w+").close()
        gpg.sign_file(self.config, "image-signing", first)

        # # Second file
        second = os.path.join(self.config.publish_path,
                              "old/device/version-1234.tar.xz")

        tools.generate_version_tarball(self.config, "old", "test", "1234",
                                       second.replace(".xz", ""))
        tools.xz_compress(second.replace(".xz", ""))
        os.remove(second.replace(".xz", ""))
        gpg.sign_file(self.config, "image-signing", second)

        with open(second.replace(".tar.xz", ".json"), "w+") as fd:
            metadata = {}
            metadata['channel.ini'] = {}
            metadata['channel.ini']['version_detail'] = "test"
            fd.write(json.dumps(metadata))
        gpg.sign_file(self.config, "image-signing",
                      second.replace(".tar.xz", ".json"))

        # # Adding the entry
        device = test_tree.get_device("old", "device")
        device.create_image(
            "full", 1234, "abc",
            ["old/device/full", "old/device/version-1234.tar.xz"])
        device.set_phased_percentage(1234, 50)

        # Rename
        os.makedirs(os.path.join(self.config.publish_path, "test/new"))
        self.assertRaises(Exception, test_tree.rename_channel, "old",
                          "test/new")
        os.rmdir(os.path.join(self.config.publish_path, "test/new"))
        os.rmdir(os.path.join(self.config.publish_path, "test"))

        self.assertTrue(test_tree.rename_channel("old", "test/new"))

        self.assertEqual(
            test_tree.list_channels()['test/new'],
            {'devices': {
                'device': {
                    'index': '/test/new/device/index.json'
                }
            }})
Exemple #4
0
    def test_channels(self):
        # Test getting a tree instance
        test_tree = tree.Tree(self.config)
        self.assertEqual(test_tree.list_channels(), {})

        # Test publishing a keyring
        keyring = gpg.Keyring(self.config, "image-signing")
        keyring.set_metadata("image-signing")
        keyring.import_keys(
            os.path.join(self.config.gpg_key_path, "image-signing"))
        self.assertRaises(Exception, test_tree.publish_keyring,
                          "image-signing")
        keyring_tar = keyring.generate_tarball()
        tools.xz_compress(keyring_tar)
        self.assertRaises(Exception, test_tree.publish_keyring,
                          "image-signing")
        gpg.sign_file(self.config, "image-master", "%s.xz" % keyring_tar)
        test_tree.publish_keyring("image-signing")

        self.assertTrue(
            os.path.exists(
                os.path.join(self.config.publish_path, "gpg", "image-signing"
                             ".tar.xz")))

        self.assertTrue(
            os.path.exists(
                os.path.join(self.config.publish_path, "gpg", "image-signing"
                             ".tar.xz.asc")))

        # Test invalid tree path
        self.assertRaises(Exception, tree.Tree, self.config,
                          os.path.join(self.temp_directory, "invalid"))

        # Test channel creation
        test_tree.create_channel("first")
        test_tree.create_channel("second")

        self.assertRaises(KeyError, test_tree.create_channel, "second")

        # Test channel removal
        test_tree.remove_channel("first")
        test_tree.remove_channel("second")

        self.assertRaises(KeyError, test_tree.remove_channel, "second")

        # Test invalid json
        with open(test_tree.indexpath, "w+") as fd:
            fd.write("[]")

        self.assertRaises(TypeError, test_tree.list_channels)

        with open(test_tree.indexpath, "w+") as fd:
            fd.write("{'a': 'a'}")
        self.assertRaises(ValueError, test_tree.list_channels)

        os.remove(test_tree.indexpath)

        # Test hidding a channel
        test_tree.create_channel("testing")
        self.assertEqual(test_tree.list_channels(),
                         {'testing': {
                             'devices': {}
                         }})

        test_tree.hide_channel("testing")
        self.assertEqual(test_tree.list_channels(),
                         {'testing': {
                             'devices': {},
                             'hidden': True
                         }})

        test_tree.show_channel("testing")
        self.assertEqual(test_tree.list_channels(),
                         {'testing': {
                             'devices': {}
                         }})

        self.assertRaises(KeyError, test_tree.hide_channel, "invalid")
        self.assertRaises(KeyError, test_tree.show_channel, "invalid")
        test_tree.remove_channel("testing")

        # Test device creation
        test_tree.create_channel("testing")
        test_tree.create_device("testing", "test")

        self.assertTrue(
            os.path.exists(
                os.path.join(self.config.publish_path, "testing", "test",
                             "index.json")))

        self.assertEqual(
            test_tree.list_channels(), {
                'testing': {
                    'devices': {
                        'test': {
                            'index': '/testing/test/'
                            'index.json'
                        }
                    }
                }
            })

        self.assertRaises(KeyError, test_tree.create_device, "invalid", "test")
        self.assertRaises(KeyError, test_tree.create_device, "testing", "test")

        # Test listing devices
        self.assertRaises(KeyError, test_tree.list_devices, "invalid")
        self.assertEqual(test_tree.list_devices("testing"), ["test"])

        # Test the index generation
        os.mkdir(os.path.join(self.config.publish_path, "testing", "empty"))
        self.assertRaises(Exception, test_tree.generate_index)
        test_tree.generate_index("I know what I'm doing")
        self.assertEqual(
            test_tree.list_channels(), {
                'testing': {
                    'devices': {
                        'test': {
                            'index': '/testing/test/'
                            'index.json'
                        }
                    }
                }
            })

        device_keyring = os.path.join(self.config.publish_path, "testing",
                                      "test", "device.tar.xz")
        open(device_keyring, "w+").close()

        test_tree.generate_index("I know what I'm doing")
        self.assertEqual(
            test_tree.list_channels(), {
                'testing': {
                    'devices': {
                        'test': {
                            'index': '/testing/test/'
                            'index.json'
                        }
                    }
                }
            })

        gpg.sign_file(self.config, "image-signing", device_keyring)
        test_tree.generate_index("I know what I'm doing")
        self.assertEqual(
            test_tree.list_channels(), {
                'testing': {
                    'devices': {
                        'test': {
                            'index': '/testing/test/index.json',
                            'keyring': {
                                'path':
                                '/testing/test/'
                                'device.tar.xz',
                                'signature':
                                '/testing/test/'
                                'device.tar'
                                '.xz.asc'
                            }
                        }
                    }
                }
            })

        # Test grabbing a device entry
        self.assertRaises(KeyError, test_tree.get_device, "invalid", "test")
        self.assertRaises(KeyError, test_tree.get_device, "testing", "invalid")
        self.assertTrue(
            isinstance(test_tree.get_device("testing", "test"), tree.Device))

        # Test device removal
        test_tree.create_device("testing", "to-remove")
        self.assertTrue(
            os.path.exists(
                os.path.join(self.config.publish_path, "testing", "to-remove",
                             "index.json")))
        self.assertRaises(KeyError, test_tree.remove_device, "invalid", "test")
        self.assertRaises(KeyError, test_tree.remove_device, "testing",
                          "invalid")
        test_tree.remove_device("testing", "to-remove")
        self.assertFalse(
            os.path.exists(
                os.path.join(self.config.publish_path, "testing", "to-remove",
                             "index.json")))

        self.assertEqual(
            test_tree.list_channels(), {
                'testing': {
                    'devices': {
                        'test': {
                            'index': '/testing/test/index.json',
                            'keyring': {
                                'path': '/testing/test/device'
                                '.tar.xz',
                                'signature': '/testing/test/devi'
                                'ce.tar.xz.asc'
                            }
                        }
                    }
                }
            })

        # Test setting the device keyring
        self.assertRaises(KeyError, test_tree.set_device_keyring, "invalid",
                          "test", "invalid")
        self.assertRaises(KeyError, test_tree.set_device_keyring, "testing",
                          "invalid", "invalid")
        test_tree.set_device_keyring("testing", "test",
                                     "testing/test/device.tar.xz")
        self.assertRaises(Exception, test_tree.set_device_keyring, "testing",
                          "test", "invalid")

        unsigned_path = os.path.join(self.config.publish_path, "unsigned")
        open(unsigned_path, "w+").close()
        self.assertRaises(Exception, test_tree.set_device_keyring, "testing",
                          "test", "unsigned")
        os.remove(unsigned_path)
Exemple #5
0
    def test_per_device_redirect(self):
        """ """
        test_tree = tree.Tree(self.config)

        # Create some channels
        test_tree.create_channel("parent")
        test_tree.create_channel("redirect")

        # Create the required devices
        test_tree.create_device("parent", "device")
        test_tree.create_device("parent", "other")
        test_tree.create_device("redirect", "other")

        # Test standard failure cases
        self.assertRaises(KeyError,
                          test_tree.create_per_device_channel_redirect,
                          "device", "redirect1", "parent")
        self.assertRaises(KeyError,
                          test_tree.create_per_device_channel_redirect,
                          "other", "redirect", "parent")
        self.assertRaises(KeyError,
                          test_tree.create_per_device_channel_redirect,
                          "device", "redirect", "parent1")
        self.assertRaises(KeyError,
                          test_tree.create_per_device_channel_redirect,
                          "device2", "redirect", "parent")

        # Create the device channel redirect
        test_tree.create_per_device_channel_redirect("device", "redirect",
                                                     "parent")

        # Publish an image

        # # First file
        first = os.path.join(self.config.publish_path, "parent/device/full")
        open(first, "w+").close()
        gpg.sign_file(self.config, "image-signing", first)

        # # Second file
        second = os.path.join(self.config.publish_path,
                              "parent/device/version-1234.tar.xz")

        tools.generate_version_tarball(self.config, "parent", "test", "1234",
                                       second.replace(".xz", ""))
        tools.xz_compress(second.replace(".xz", ""))
        os.remove(second.replace(".xz", ""))
        gpg.sign_file(self.config, "image-signing", second)

        with open(second.replace(".tar.xz", ".json"), "w+") as fd:
            metadata = {}
            metadata['channel.ini'] = {}
            metadata['channel.ini']['version_detail'] = "test"
            fd.write(json.dumps(metadata))
        gpg.sign_file(self.config, "image-signing",
                      second.replace(".tar.xz", ".json"))

        # # Adding the entry
        device = test_tree.get_device("parent", "device")
        device.create_image(
            "full", 1234, "abc",
            ["parent/device/full", "parent/device/version-1234.tar.xz"])
        device.set_phased_percentage(1234, 50)

        # # Get the target
        target = test_tree.get_device("redirect", "device")

        # Confirm the fs layout
        self.assertTrue(
            os.path.exists(os.path.join(self.config.publish_path, "redirect")))
        self.assertFalse(
            os.path.exists(
                os.path.join(self.config.publish_path, "redirect", "device")))
        self.assertTrue(
            os.path.exists(
                os.path.join(self.config.publish_path, "parent", "device")))
        self.assertEqual(device.list_images(), target.list_images())

        # Check the channels index
        channels = test_tree.list_channels()
        self.assertIn("other", channels['redirect']['devices'])
        self.assertEqual(channels['redirect']['devices']['other']['index'],
                         "/redirect/other/index.json")
        self.assertIn("device", channels['redirect']['devices'])
        self.assertEqual(channels['redirect']['devices']['device']['index'],
                         "/parent/device/index.json")
        self.assertIn("redirect", channels['redirect']['devices']['device'])

        # Try removing a per-channel redirect
        self.assertTrue(test_tree.remove_device("redirect", "device"))

        # Confirm files are not removed
        self.assertTrue(
            os.path.exists(
                os.path.join(self.config.publish_path, "parent", "device",
                             "index.json")))
Exemple #6
0
    def test_alias(self):
        test_tree = tree.Tree(self.config)

        # Create some channels and aliases
        test_tree.create_channel("parent")
        test_tree.create_channel_alias("alias", "parent")
        test_tree.create_channel_redirect("redirect", "alias")

        # Test standard failure cases
        self.assertRaises(KeyError, test_tree.create_channel_alias, "alias",
                          "parent")

        self.assertRaises(KeyError, test_tree.create_channel_alias, "alias1",
                          "parent1")

        self.assertRaises(KeyError, test_tree.change_channel_alias, "alias1",
                          "parent")

        self.assertRaises(KeyError, test_tree.change_channel_alias, "alias",
                          "parent1")

        self.assertRaises(KeyError, test_tree.change_channel_alias, "parent",
                          "parent")

        self.assertRaises(KeyError, test_tree.change_channel_alias, "redirect",
                          "parent")

        self.assertRaises(KeyError, test_tree.sync_aliases, "missing")
        self.assertRaises(KeyError, test_tree.sync_alias, "missing")
        self.assertRaises(TypeError, test_tree.sync_alias, "parent")

        test_tree.remove_channel("parent")
        self.assertRaises(KeyError, test_tree.sync_alias, "alias")
        test_tree.create_channel("parent")

        # Publish a basic image
        test_tree.create_device("parent", "device")
        test_tree.create_device("parent", "device2")
        test_tree.create_device("parent", "device3")
        test_tree.create_device("alias", "device1")

        # # First file
        first = os.path.join(self.config.publish_path, "parent/device/full")
        open(first, "w+").close()
        gpg.sign_file(self.config, "image-signing", first)

        # # Second file
        second = os.path.join(self.config.publish_path,
                              "parent/device/version-1234.tar.xz")

        tools.generate_version_tarball(self.config, "parent", "test", "1234",
                                       second.replace(".xz", ""))
        tools.xz_compress(second.replace(".xz", ""))
        os.remove(second.replace(".xz", ""))
        gpg.sign_file(self.config, "image-signing", second)

        with open(second.replace(".tar.xz", ".json"), "w+") as fd:
            metadata = {}
            metadata['channel.ini'] = {}
            metadata['channel.ini']['version_detail'] = "test"
            fd.write(json.dumps(metadata))
        gpg.sign_file(self.config, "image-signing",
                      second.replace(".tar.xz", ".json"))

        # # Adding the entry
        device = test_tree.get_device("parent", "device")
        device.create_image(
            "full", 1234, "abc",
            ["parent/device/full", "parent/device/version-1234.tar.xz"])
        device.set_phased_percentage(1234, 50)

        # # Adding a fake entry to the alias channel
        device = test_tree.get_device("alias", "device")
        device.create_image(
            "full", 1235, "abc",
            ["parent/device/full", "parent/device/version-1234.tar.xz"])

        # Sync the alises
        device3 = test_tree.get_device("alias", "device3")
        shutil.rmtree(device3.path)
        test_tree.sync_aliases("parent")

        alias_device = test_tree.get_device("alias", "device")
        self.assertEqual(alias_device.get_phased_percentage(1234), 50)

        test_tree.create_channel("new_parent")
        test_tree.change_channel_alias("alias", "new_parent")

        test_tree.remove_channel("alias")
        test_tree.remove_channel("new_parent")
        test_tree.remove_channel("parent")
    def test_generate_delta(self):
        # Source tarball
        source_path = os.path.join(self.temp_directory, "source.tar")
        source_path_xz = "%s.xz" % source_path
        source_tar = tarfile.open(source_path, "w")
        source_tar.close()
        tools.xz_compress(source_path)
        os.remove(source_path)

        # Source json
        with open(os.path.join(self.temp_directory, "source.json"),
                  "w+") as fd:
            source_json = {}
            source_json['a'] = 1
            fd.write(json.dumps(source_json))

        # Destination tarball
        destination_path = os.path.join(self.temp_directory, "destination.tar")
        destination_path_xz = "%s.xz" % destination_path
        destination_tar = tarfile.open(destination_path, "w")
        destination_tar.close()
        tools.xz_compress(destination_path)
        os.remove(destination_path)

        # Destination json
        with open(os.path.join(self.temp_directory, "destination.json"),
                  "w+") as fd:
            source_json = {}
            source_json['b'] = 2
            fd.write(json.dumps(source_json))

        # Check that version tarballs are just returned
        open(os.path.join(self.temp_directory,
                          "version-1.tar.xz"), "w+").close()
        open(os.path.join(self.temp_directory,
                          "version-2.tar.xz"), "w+").close()
        self.assertEqual(
            generators.generate_delta(
                self.config,
                os.path.join(self.temp_directory, "version-1.tar.xz"),
                os.path.join(self.temp_directory, "version-2.tar.xz")),
            os.path.join(self.temp_directory, "version-2.tar.xz"))

        # Check that keyring tarballs are just returned
        open(os.path.join(self.temp_directory,
                          "keyring-1.tar.xz"), "w+").close()
        self.assertEqual(
            generators.generate_delta(
                self.config,
                os.path.join(self.temp_directory, "keyring-1.tar.xz"),
                os.path.join(self.temp_directory, "keyring-1.tar.xz")),
            os.path.join(self.temp_directory, "keyring-1.tar.xz"))

        # Generate the diff
        self.assertEqual(
            generators.generate_delta(self.config, source_path_xz,
                                      destination_path_xz),
            os.path.join(self.config.publish_path, "pool",
                         "destination.delta-source.tar.xz"))

        # Check that we get cached entries
        generators.generate_delta(self.config, source_path_xz,
                                  destination_path_xz)