示例#1
0
def generate_version_metadata(config,
                              version,
                              channel,
                              device,
                              path,
                              version_detail=""):
    """
        Helper function that will take selected version info and create
        the .json version file for the corresponding version tarball.
    """

    metadata = {}
    metadata['generator'] = "version"
    metadata['version'] = version
    metadata['version_detail'] = "version=%s" % version
    metadata['channel.ini'] = {}
    metadata['channel.ini']['channel'] = channel
    metadata['channel.ini']['device'] = device
    metadata['channel.ini']['version'] = str(version)
    metadata['channel.ini']['version_detail'] = version_detail

    with open(path.replace(".tar.xz", ".json"), "w+") as fd:
        fd.write("%s\n" % json.dumps(
            metadata, sort_keys=True, indent=4, separators=(",", ": ")))
    gpg.sign_file(config, "image-signing", path.replace(".tar.xz", ".json"))
    def _publish_dummy_to_channel(self, device):
        """Helper function used to publish a dummy image for selected device"""
        open(os.path.join(self.config.publish_path, "file-1.tar.xz"),
             "w+").close()

        with open(os.path.join(self.config.publish_path, "file-1.json"),
                  "w+") as fd:
            fd.write(json.dumps({'version_detail': "abcd"}))

        gpg.sign_file(self.config, "image-signing",
                      os.path.join(self.config.publish_path, "file-1.tar.xz"))
        device.create_image("full", 1234, "abc", ["file-1.tar.xz"],
                            minversion=1233, bootme=True)
示例#3
0
def channels_json(config, path, commit=False):
    """
        Context function (to be used with "with") that will open a
        channels.json file, parse it, validate it and return the
        decoded version.

        If commit is True, the file will then be updated (or created) on
        exit.
    """

    # If the file doesn't exist, just yield an empty dict
    json_content = {}
    if os.path.exists(path):
        with open(path, "r") as fd:
            content = fd.read()
            if content:
                json_content = json.loads(content)

    # Validation
    if not isinstance(json_content, dict):
        raise TypeError("Invalid channels.json, not a dict.")

    if commit:
        orig_json_content = copy.deepcopy(json_content)

    # Yield the decoded value and save on exit
    try:
        yield json_content
    finally:
        if commit and (orig_json_content != json_content or
                       not os.path.exists(path)):
            new_path = "%s.new" % path
            with open(new_path, "w+") as fd:
                fd.write("%s\n" % json.dumps(json_content, sort_keys=True,
                                             indent=4, separators=(",", ": ")))

            # Move the signature
            gpg.sign_file(config, "image-signing", new_path)
            if os.path.exists("%s.asc" % path):
                os.remove("%s.asc" % path)
            os.rename("%s.asc" % new_path, "%s.asc" % path)

            # Move the index
            if os.path.exists(path):
                os.remove(path)
            os.rename(new_path, path)
示例#4
0
    def test_expiry(self):
        test_tree = tree.Tree(self.config)
        test_tree.create_channel("test")
        test_tree.create_device("test", "test")

        # Insert a few images
        image_path = os.path.join(test_tree.path, "test", "test",
                                  "image.tar.xz")
        open(image_path, "w+").close()
        gpg.sign_file(self.config, "image-signing", image_path)
        device = test_tree.get_device("test", "test")
        device.create_image("full", 1, "test", [image_path])
        device.create_image("full", 2, "test", [image_path])
        device.create_image("delta", 2, "test", [image_path], base=1)
        device.create_image("full", 3, "test", [image_path])
        device.create_image("delta", 3, "test", [image_path], base=2)
        device.create_image("delta", 3, "test", [image_path], base=1)
        device.create_image("full", 4, "test", [image_path])
        device.create_image("delta", 4, "test", [image_path], base=3)
        device.create_image("delta", 4, "test", [image_path], base=2)
        device.create_image("delta", 4, "test", [image_path], base=1)

        self.assertEqual(len(device.list_images()), 10)

        device.expire_images(10)
        self.assertEqual(len(device.list_images()), 10)

        device.expire_images(3)
        self.assertEqual(len(device.list_images()), 6)

        device.expire_images(3)
        self.assertEqual(len(device.list_images()), 6)

        device.expire_images(2)
        self.assertEqual(len(device.list_images()), 3)

        device.expire_images(1)
        self.assertEqual(len(device.list_images()), 1)

        device.expire_images(0)
        self.assertEqual(len(device.list_images()), 0)
示例#5
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())
示例#6
0
    def test_phased_percentage(self):
        test_tree = tree.Tree(self.config)

        # Create a channel, device and images
        test_tree.create_channel("test")
        test_tree.create_device("test", "test")

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

        # # Adding a first entry
        device = test_tree.get_device("test", "test")
        device.create_image("full", 1234, "abc", ["test/test/full"])
        device.set_phased_percentage(1234, 20)
        self.assertEqual(device.get_phased_percentage(1234), 20)

        # # Adding a second entry
        device = test_tree.get_device("test", "test")
        device.create_image("full", 1235, "abc", ["test/test/full"])

        self.assertEqual(device.get_phased_percentage(1235), 100)
        device.set_phased_percentage(1235, 0)

        device.set_phased_percentage(1235, 100)

        # Test standard failure cases
        self.assertRaises(TypeError, device.set_phased_percentage, 1235,
                          "invalid")
        self.assertRaises(TypeError, device.set_phased_percentage, 1235, 10.5)
        self.assertRaises(ValueError, device.set_phased_percentage, 1235, 101)
        self.assertRaises(ValueError, device.set_phased_percentage, 1235, -1)

        self.assertRaises(IndexError, device.set_phased_percentage, 4242, 50)
        self.assertRaises(Exception, device.set_phased_percentage, 1234, 50)

        self.assertRaises(IndexError, device.get_phased_percentage, 4242)
示例#7
0
    def test_file_lists(self):
        test_tree = tree.Tree(self.config)
        test_tree.create_channel("test")
        test_tree.create_device("test", "test")

        self.assertEqual(test_tree.list_missing_files(), [])
        self.assertEqual(test_tree.list_orphaned_files(), [])

        # Confirm that the gpg directory is ignored
        if not os.path.exists(os.path.join(test_tree.path, "gpg")):
            os.mkdir(os.path.join(test_tree.path, "gpg"))
        self.assertEqual(test_tree.list_orphaned_files(), [])

        # Confirm that it picks up extra directories
        os.mkdir(os.path.join(test_tree.path, "invalid"))
        self.assertEqual(test_tree.list_orphaned_files(),
                         [os.path.join(test_tree.path, "invalid")])

        # And that it gets cleared up
        test_tree.cleanup_tree()
        self.assertEqual(test_tree.list_orphaned_files(), [])

        # Confirm that it picks up extra files
        open(os.path.join(test_tree.path, "invalid"), "w+").close()
        self.assertEqual(test_tree.list_orphaned_files(),
                         [os.path.join(test_tree.path, "invalid")])

        # And that it gets cleared up
        test_tree.cleanup_tree()
        self.assertEqual(test_tree.list_orphaned_files(), [])

        # Test that keyrings aren't considered as orphaned files
        keyring_path = os.path.join(test_tree.path, "gpg", "device.tar.xz")
        open(keyring_path, "w+").close()
        gpg.sign_file(self.config, "image-signing", keyring_path)
        test_tree.set_device_keyring("test", "test", keyring_path)
        self.assertEqual(test_tree.list_orphaned_files(), [])

        # Test that images aren't considered as orphaned files
        image_path = os.path.join(test_tree.path, "test", "test",
                                  "image.tar.xz")
        open(image_path, "w+").close()
        gpg.sign_file(self.config, "image-signing", image_path)
        open(image_path.replace(".tar.xz", ".json"), "w+").close()
        gpg.sign_file(self.config, "image-signing",
                      image_path.replace(".tar.xz", ".json"))
        device = test_tree.get_device("test", "test")
        device.create_image("full", 12345, "test", [image_path])
        self.assertEqual(test_tree.list_orphaned_files(), [])
示例#8
0
    def test_sign_file(self):
        test_string = "test-string"

        test_file = os.path.join(self.temp_directory, "test.txt")
        with open(test_file, "w+") as fd:
            fd.write(test_string)

        # Detached armored signature
        [os.remove(path) for path in glob.glob("%s.*" % test_file)]
        self.assertTrue(gpg.sign_file(self.config, "image-signing", test_file))
        self.assertTrue(os.path.exists("%s.asc" % test_file))

        # Detached binary signature
        [os.remove(path) for path in glob.glob("%s.*" % test_file)]
        self.assertTrue(
            gpg.sign_file(self.config, "image-signing", test_file,
                          armor=False))
        self.assertTrue(os.path.exists("%s.sig" % test_file))

        # Standard armored signature
        [os.remove(path) for path in glob.glob("%s.*" % test_file)]
        self.assertTrue(
            gpg.sign_file(self.config,
                          "image-signing",
                          test_file,
                          detach=False))
        self.assertTrue(os.path.exists("%s.asc" % test_file))

        # Standard binary signature
        [os.remove(path) for path in glob.glob("%s.*" % test_file)]
        self.assertTrue(
            gpg.sign_file(self.config,
                          "image-signing",
                          test_file,
                          detach=False,
                          armor=False))
        self.assertTrue(os.path.exists("%s.gpg" % test_file))

        # Failure cases
        self.assertRaises(Exception, gpg.sign_file, self.config, "invalid",
                          test_file)
        [os.remove(path) for path in glob.glob("%s.*" % test_file)]
        gpg.sign_file(self.config, "image-signing", test_file)
        self.assertRaises(Exception, gpg.sign_file, self.config,
                          "image-signing", test_file)
        self.assertRaises(Exception, gpg.sign_file, self.config,
                          "image-signing", "invalid")
示例#9
0
def index_json(config, path, commit=False):
    """
        Context function (to be used with "with") that will open an
        index.json file, parse it, validate it and return the
        decoded version.

        If commit is True, the file will then be updated (or created) on
        exit.
    """

    # If the file doesn't exist, just yield an empty dict
    json_content = {}
    json_content['global'] = {}
    json_content['images'] = []

    if os.path.exists(path):
        with open(path, "r") as fd:
            content = fd.read()
            if content:
                json_content = json.loads(content)

    # Validation
    if not isinstance(json_content, dict):
        raise TypeError("Invalid index.json, not a dict.")

    if commit:
        orig_json_content = copy.deepcopy(json_content)

    # Yield the decoded value and save on exit
    try:
        yield json_content
    finally:
        # Remove any invalid attribute
        versions = sorted({image['version']
                           for image in json_content['images']})
        if versions:
            last_version = versions[-1]

            # Remove phased-percentage from any old image
            for image in json_content['images']:
                if image['version'] != last_version and \
                        "phased-percentage" in image:
                    image.pop("phased-percentage")

        # Save to disk
        if commit and (orig_json_content != json_content or
                       not os.path.exists(path)):
            json_content['global']['generated_at'] = time.strftime(
                "%a %b %d %H:%M:%S UTC %Y", time.gmtime())

            new_path = "%s.new" % path
            with open(new_path, "w+") as fd:
                fd.write("%s\n" % json.dumps(json_content, sort_keys=True,
                                             indent=4, separators=(",", ": ")))

            # Move the signature
            gpg.sign_file(config, "image-signing", new_path)
            if os.path.exists("%s.asc" % path):
                os.remove("%s.asc" % path)
            os.rename("%s.asc" % new_path, "%s.asc" % path)

            # Move the index
            if os.path.exists(path):
                os.remove(path)
            os.rename(new_path, path)
示例#10
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
示例#11
0
    def test_index(self):
        device = tree.Device(self.config, self.temp_directory)

        # Check without files
        self.assertRaises(Exception, device.create_image, "full", 1234, "test",
                          [])

        # Check with missing file
        self.assertRaises(Exception, device.create_image, "full", 1234, "test",
                          ["file"])

        # Check with missing signature
        open(os.path.join(self.config.publish_path, "file"), "w+").close()
        self.assertRaises(Exception, device.create_image, "full", 1234, "test",
                          ["file"])

        # Check with missing base version
        gpg.sign_file(self.config, "image-signing",
                      os.path.join(self.config.publish_path, "file"))
        self.assertRaises(KeyError, device.create_image, "delta", 1234, "test",
                          ["file"])

        # Check with extra base version
        self.assertRaises(KeyError,
                          device.create_image,
                          "full",
                          1234,
                          "test", ["file"],
                          base=1233)

        # Check with extra minimum version
        self.assertRaises(KeyError,
                          device.create_image,
                          "delta",
                          1234,
                          "test", ["file"],
                          base=1233,
                          minversion=1233)

        # Valid full image
        open(os.path.join(self.config.publish_path, "second"), "w+").close()
        gpg.sign_file(self.config, "image-signing",
                      os.path.join(self.config.publish_path, "second"))
        device.create_image("full",
                            1234,
                            "abc", ["file", "second"],
                            minversion=1233,
                            bootme=True)

        # Valid delta image
        device.create_image("delta",
                            1234,
                            "abc", ["file", "second"],
                            base=1233,
                            bootme=True,
                            version_detail="blah")

        # Check the image list
        self.assertEqual(device.list_images(), [{
            'bootme':
            True,
            'description':
            'abc',
            'minversion':
            1233,
            'type':
            'full',
            'version':
            1234,
            'files': [{
                'signature': '/file.asc',
                'path': '/file',
                'checksum': 'e3b0c44298fc1c149afbf4c8996fb924'
                '27ae41e4649b934ca495991b7852b855',
                'size': 0,
                'order': 0
            }, {
                'signature': '/second.asc',
                'path': '/second',
                'checksum': 'e3b0c44298fc1c149afbf4c8996fb924'
                '27ae41e4649b934ca495991b7852b855',
                'size': 0,
                'order': 1
            }]
        }, {
            'bootme':
            True,
            'description':
            'abc',
            'type':
            'delta',
            'base':
            1233,
            'version':
            1234,
            'version_detail':
            "blah",
            'files': [{
                'signature': '/file.asc',
                'path': '/file',
                'checksum': 'e3b0c44298fc1c149afbf4c8996fb924'
                '27ae41e4649b934ca495991b7852b855',
                'size': 0,
                'order': 0
            }, {
                'signature': '/second.asc',
                'path': '/second',
                'checksum': 'e3b0c44298fc1c149afbf4c8996fb924'
                '27ae41e4649b934ca495991b7852b855',
                'size': 0,
                'order': 1
            }]
        }])

        # Set descriptions
        device.set_description("delta", 1234, "test", {"fr": "essai"}, 1233)
        entry = device.get_image("delta", 1234, 1233)
        self.assertEqual(entry['description'], "test")
        self.assertEqual(entry['description_fr'], "essai")

        self.assertRaises(TypeError, device.set_description, "delta", 1234,
                          "test", ['test'], 1233)

        # Remove the images
        device.remove_image("delta", 1234, 1233)
        device.remove_image("full", 1234)
        self.assertEqual(device.list_images(), [])

        # Error case of remove_image
        self.assertRaises(ValueError, device.remove_image, "invalid", 1234)
        self.assertRaises(ValueError, device.remove_image, "delta", 1234)
        self.assertRaises(IndexError, device.remove_image, "delta", 1234, 1232)

        # Test invalid json
        with open(device.indexpath, "w+") as fd:
            fd.write("test")

        self.assertRaises(ValueError, device.list_images)

        with open(device.indexpath, "w+") as fd:
            fd.write("[]")

        self.assertRaises(TypeError, device.list_images)
示例#12
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'
                }
            }})
示例#13
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)
示例#14
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")))
示例#15
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")