Example #1
0
def push(snap_filename, release_channels=None):
    """Push a snap_filename to the store.

    If a cached snap is available, a delta will be generated from
    the cached snap to the new target snap and uploaded instead. In the
    case of a delta processing or upload failure, push will fall back to
    uploading the full snap.

    If release_channels is defined it also releases it to those channels if the
    store deems the uploaded snap as ready to release.
    """
    snap_yaml = _get_data_from_snap_file(snap_filename)
    snap_name = snap_yaml['name']
    store = storeapi.StoreClient()

    logger.info('Preparing to push {!r} to the store.'.format(snap_filename))
    with _requires_login():
        store.push_precheck(snap_name)

    snap_cache = cache.SnapCache(project_name=snap_name)
    arch = snap_yaml['architectures'][0]
    source_snap = snap_cache.get(deb_arch=arch)

    sha3_384_available = hasattr(hashlib, 'sha3_384')

    if sha3_384_available and source_snap:
        try:
            result = _push_delta(snap_name, snap_filename, source_snap)
        except storeapi.errors.StoreDeltaApplicationError as e:
            logger.warning('Error generating delta: {}\n'
                           'Falling back to pushing full snap...'.format(
                               str(e)))
            result = _push_snap(snap_name, snap_filename)
        except storeapi.errors.StorePushError as e:
            store_error = e.error_list[0].get('message')
            logger.warning(
                'Unable to push delta to store: {}\n'
                'Falling back to pushing full snap...'.format(store_error))
            result = _push_snap(snap_name, snap_filename)
    else:
        result = _push_snap(snap_name, snap_filename)

    # This is workaround until LP: #1599875 is solved
    if 'revision' in result:
        logger.info('Revision {!r} of {!r} created.'.format(
            result['revision'], snap_name))

        snap_cache.cache(snap_filename=snap_filename)
        snap_cache.prune(deb_arch=arch,
                         keep_hash=calculate_sha3_384(snap_filename))
    else:
        logger.info('Pushing {!r}'.format(snap_name))

    if release_channels:
        release(snap_name, result['revision'], release_channels)
Example #2
0
def push(snap_filename, release_channels=None):
    """Push a snap_filename to the store.

    If a cached snap is available, a delta will be generated from
    the cached snap to the new target snap and uploaded instead. In the
    case of a delta processing or upload failure, push will fall back to
    uploading the full snap.

    If release_channels is defined it also releases it to those channels if the
    store deems the uploaded snap as ready to release.
    """
    snap_yaml = _get_data_from_snap_file(snap_filename)
    snap_name = snap_yaml["name"]
    store = storeapi.StoreClient()

    logger.debug("Run push precheck and verify cached data for {!r}.".format(
        snap_filename))
    with _requires_login():
        store.push_precheck(snap_name)

    snap_cache = cache.SnapCache(project_name=snap_name)
    arch = "all"

    with contextlib.suppress(KeyError):
        arch = snap_yaml["architectures"][0]

    source_snap = snap_cache.get(deb_arch=arch)
    sha3_384_available = hasattr(hashlib, "sha3_384")

    if sha3_384_available and source_snap:
        try:
            result = _push_delta(snap_name, snap_filename, source_snap)
        except storeapi.errors.StoreDeltaApplicationError as e:
            logger.warning("Error generating delta: {}\n"
                           "Falling back to pushing full snap...".format(
                               str(e)))
            result = _push_snap(snap_name, snap_filename)
        except storeapi.errors.StorePushError as e:
            store_error = e.error_list[0].get("message")
            logger.warning(
                "Unable to push delta to store: {}\n"
                "Falling back to pushing full snap...".format(store_error))
            result = _push_snap(snap_name, snap_filename)
    else:
        result = _push_snap(snap_name, snap_filename)

    logger.info("Revision {!r} of {!r} created.".format(
        result["revision"], snap_name))

    snap_cache.cache(snap_filename=snap_filename)
    snap_cache.prune(deb_arch=arch,
                     keep_hash=calculate_sha3_384(snap_filename))

    if release_channels:
        release(snap_name, result["revision"], release_channels)
Example #3
0
    def test_snap_cache(self):
        # cache snap
        snap_cache = cache.SnapCache(project_name='cache-test')
        cached_snap_path = snap_cache.cache(snap_filename=self.snap_path)

        expected_snap_path = os.path.join(
            snap_cache.snap_cache_root, 'amd64',
            file_utils.calculate_sha3_384(self.snap_path))

        self.assertThat(cached_snap_path, Equals(expected_snap_path))
        self.assertTrue(os.path.isfile(cached_snap_path))
Example #4
0
    def test_snap_cache_get_latest(self):
        self.useFixture(fixture_setup.FakeTerminal())

        # Create snaps
        with open(os.path.join(self.path, 'snapcraft.yaml'), 'w') as f:
            f.write("""name: my-snap-name
summary: test cached snap
description: test cached snap
architectures: ['{}']
confinement: devmode
grade: devel
version: '0.1'

parts:
    my-part:
      plugin: nil
""".format(self.deb_arch))
        main(['snap'])

        snap_file = glob.glob('*0.1*.snap')[0]

        snap_cache = cache.SnapCache(project_name='my-snap-name')
        snap_cache.cache(snap_filename=snap_file)

        with open(os.path.join(self.path, 'snapcraft.yaml'), 'w') as f:
            f.write("""name: my-snap-name
summary: test cached snap
description: test cached snap
architectures: ['{}']
confinement: devmode
grade: devel
version: '0.2'

parts:
    my-part:
      plugin: nil
""".format(self.deb_arch))
        main(['snap'])

        snap_file_latest = glob.glob('*0.2*.snap')[0]

        snap_cache.cache(snap_filename=snap_file_latest)
        latest_hash = file_utils.calculate_sha3_384(snap_file_latest)

        # get latest
        latest_snap = snap_cache.get(deb_arch=self.deb_arch)

        expected_snap_path = os.path.join(
            snap_cache.snap_cache_root,
            self.deb_arch,
            latest_hash
        )

        self.assertEqual(expected_snap_path, latest_snap)
Example #5
0
    def test_prune_snap_cache(self):
        self.useFixture(fixture_setup.FakeTerminal())
        snap_cache = cache.SnapCache(project_name='my-snap-name')

        snap_revision = 9
        snap_file = 'my-snap-name_0.1_amd64.snap'

        # create dummy snap
        open(os.path.join(self.path, snap_file), 'a').close()

        # cache snap
        snap_cache.cache(snap_file, snap_revision)

        # create other cached snap revisions
        to_be_deleted_files = []
        cached_snaps = [
            'a-cached-snap_0.3_amd64_8.snap',
            'another-cached-snap_1.0_arm64_6.snap'
        ]

        for cached_snap in cached_snaps:
            cached_snap_path = os.path.join(snap_cache.snap_cache_dir,
                                            cached_snap)
            to_be_deleted_files.append(cached_snap_path)
            open(cached_snap_path, 'a').close()

        real_cached_snap = _rewrite_snap_filename_with_revision(
            snap_file, snap_revision)

        # confirm expected snap cached
        self.assertEqual(3, len(os.listdir(snap_cache.snap_cache_dir)))
        self.assertTrue(
            os.path.isfile(
                os.path.join(snap_cache.snap_cache_dir, real_cached_snap)))

        if not self.valid_revision:
            with self.assertRaises(ValueError):
                snap_cache.prune(keep_revision='invalid-revision')
            with self.assertRaises(TypeError):
                snap_cache.prune(keep_revision=None)
        else:
            # prune cached snaps
            purned_file_list = snap_cache.prune(keep_revision=snap_revision)

            # confirm other snaps are purged
            self.assertEqual(set(purned_file_list), set(to_be_deleted_files))
            for snap in purned_file_list:
                self.assertFalse(os.path.isfile(snap))

            # confirm the expected cached file still exist
            self.assertEqual(1, len(os.listdir(snap_cache.snap_cache_dir)))
            self.assertTrue(
                os.path.isfile(
                    os.path.join(snap_cache.snap_cache_dir, real_cached_snap)))
Example #6
0
    def test_snap_cache(self):
        self.useFixture(fixture_setup.FakeTerminal())

        # cache snap
        snap_cache = cache.SnapCache(project_name='cache-test')
        cached_snap_path = snap_cache.cache(snap_filename=self.snap_path)

        expected_snap_path = os.path.join(
            snap_cache.snap_cache_root, 'amd64',
            file_utils.calculate_sha3_384(self.snap_path))

        self.assertEqual(expected_snap_path, cached_snap_path)
        self.assertTrue(os.path.isfile(cached_snap_path))
Example #7
0
    def test_snap_cache_get_by_hash(self):
        snap_cache = cache.SnapCache(project_name="my-snap-name")
        snap_cache.cache(snap_filename=self.snap_path)

        # get hash of snap
        snap_hash = file_utils.calculate_sha3_384(self.snap_path)

        # get snap by hash
        snap = snap_cache.get(deb_arch="amd64", snap_hash=snap_hash)

        self.assertThat(
            snap, Equals(os.path.join(snap_cache.snap_cache_root, "amd64", snap_hash))
        )
Example #8
0
    def test_snap_cache_get_by_hash(self):
        self.useFixture(fixture_setup.FakeTerminal())

        snap_cache = cache.SnapCache(project_name='my-snap-name')
        snap_cache.cache(snap_filename=self.snap_path)

        # get hash of snap
        snap_hash = file_utils.calculate_sha3_384(self.snap_path)

        # get snap by hash
        snap = snap_cache.get(deb_arch='amd64', snap_hash=snap_hash)

        self.assertEqual(
            os.path.join(snap_cache.snap_cache_root, 'amd64', snap_hash), snap)
Example #9
0
    def test_snap_cache_get_latest(self):
        # Create snaps
        meta_dir = os.path.join(self.path, "prime", "meta")
        os.makedirs(meta_dir)
        with open(os.path.join(meta_dir, "snap.yaml"), "w") as f:
            print(
                dedent("""\
                name: my-snap-name
                summary: test cached snap
                description: test cached snap
                confinement: devmode
                grade: devel
                version: '0.1'
                """),
                file=f,
            )
        result = self.run_command(["pack", os.path.join(self.path, "prime")])
        self.assertThat(result.exit_code, Equals(0))
        snap_file = glob.glob("*0.1*.snap")[0]

        snap_cache = cache.SnapCache(project_name="my-snap-name")
        snap_cache.cache(snap_filename=snap_file)

        with open(os.path.join(meta_dir, "snap.yaml"), "w") as f:
            print(
                dedent("""\
                name: my-snap-name
                summary: test cached snap
                description: test cached snap
                confinement: devmode
                grade: devel
                version: '0.2'
                """),
                file=f,
            )
        result = self.run_command(["pack", os.path.join(self.path, "prime")])
        self.assertThat(result.exit_code, Equals(0))
        snap_file_latest = glob.glob("*0.2*.snap")[0]

        snap_cache.cache(snap_filename=snap_file_latest)
        latest_hash = file_utils.calculate_sha3_384(snap_file_latest)

        # get latest
        latest_snap = snap_cache.get(deb_arch="all")

        expected_snap_path = os.path.join(snap_cache.snap_cache_root, "all",
                                          latest_hash)

        self.assertThat(latest_snap, Equals(expected_snap_path))
Example #10
0
    def test_snap_cache(self):
        self.useFixture(fixture_setup.FakeTerminal())
        snap_cache = cache.SnapCache()
        snap_file = 'my-snap-name_0.1_amd64.snap'

        # create dummy snap
        open(os.path.join(self.path, snap_file), 'a').close()

        # cache snap
        cached_snap_path = snap_cache.cache(snap_file, 10)

        _, expected_snap = os.path.split(cached_snap_path)

        self.assertEqual('my-snap-name_0.1_amd64_10.snap', expected_snap)
        self.assertTrue(os.path.isfile(cached_snap_path))
Example #11
0
    def test_prune_snap_cache(self):
        self.useFixture(
            fixtures.MockPatchObject(cache.SnapCache,
                                     "_get_snap_deb_arch",
                                     return_value="amd64"))
        # Create snaps
        snap_file_1 = pathlib.Path("snap_01_amd64.snap")
        snap_file_2 = pathlib.Path("snap_02_amd64.snap")

        for snap_file in (snap_file_1, snap_file_2):
            with snap_file.open("w") as f:
                # Add whatever content
                print(snap_file.as_posix(), file=f)

        snap_cache = cache.SnapCache(project_name="snap")
        snap_file_1_path = snap_cache.cache(snap_filename=snap_file_1)
        _, snap_file_1_hash = os.path.split(snap_file_1_path)

        snap_file_2_path = snap_cache.cache(snap_filename=snap_file_2)
        snap_file_2_dir, snap_file_2_hash = os.path.split(snap_file_2_path)

        # confirm expected snap cached
        self.assertThat(len(os.listdir(snap_file_2_dir)), Equals(2))

        # prune
        pruned_files = snap_cache.prune(deb_arch=self.deb_arch,
                                        keep_hash=snap_file_2_hash)

        self.assertThat(len(pruned_files), Equals(1))
        self.assertThat(
            pruned_files,
            Contains(
                os.path.join(snap_cache.snap_cache_root, self.deb_arch,
                             snap_file_1_hash)),
        )

        self.assertThat(
            pruned_files,
            Not(
                Contains(
                    os.path.join(snap_cache.snap_cache_root,
                                 snap_file_2_hash))),
        )
Example #12
0
    def test_snap_cache_get_latest_no_architectures(self):
        # Create snaps
        meta_dir = os.path.join(self.path, 'prime', 'meta')
        os.makedirs(meta_dir)
        with open(os.path.join(meta_dir, 'snap.yaml'), 'w') as f:
            f.write("""name: my-snap-name
summary: test cached snap
description: test cached snap
confinement: devmode
grade: devel
version: '0.1'
""")
        result = self.run_command(['pack', os.path.join(self.path, 'prime')])
        self.assertThat(result.exit_code, Equals(0))
        snap_file = glob.glob('*0.1*.snap')[0]

        snap_cache = cache.SnapCache(project_name='my-snap-name')
        snap_cache.cache(snap_filename=snap_file)

        with open(os.path.join(meta_dir, 'snap.yaml'), 'w') as f:
            f.write("""name: my-snap-name
summary: test cached snap
description: test cached snap
confinement: devmode
grade: devel
version: '0.2'
""")
        result = self.run_command(['pack', os.path.join(self.path, 'prime')])
        self.assertThat(result.exit_code, Equals(0))
        snap_file_latest = glob.glob('*0.2*.snap')[0]

        snap_cache.cache(snap_filename=snap_file_latest)
        latest_hash = file_utils.calculate_sha3_384(snap_file_latest)

        # get latest
        latest_snap = snap_cache.get(deb_arch='all')

        expected_snap_path = os.path.join(snap_cache.snap_cache_root, 'all',
                                          latest_hash)

        self.assertThat(latest_snap, Equals(expected_snap_path))
Example #13
0
    def test_snap_cache_get_by_hash(self):
        self.useFixture(fixture_setup.FakeTerminal())

        # Create snap
        main(['init'])
        main(['snap'])

        snap_file = glob.glob('*.snap')[0]

        snap_cache = cache.SnapCache(project_name='my-snap-name')
        snap_cache.cache(snap_filename=snap_file)

        # get hash of snap
        snap_hash = file_utils.calculate_sha3_384(snap_file)

        # get snap by hash
        snap = snap_cache.get(deb_arch=self.deb_arch, snap_hash=snap_hash)

        self.assertEqual(
            os.path.join(snap_cache.snap_cache_root, self.deb_arch, snap_hash),
            snap
        )
Example #14
0
    def test_snap_cache(self):
        self.useFixture(fixture_setup.FakeTerminal())

        # Create a snap
        main(['init'])
        main(['snap'])
        snap_file = glob.glob('*.snap')[0]
        snap_path = os.path.join(self.path, snap_file)

        # cache snap
        snap_cache = cache.SnapCache(project_name='cache-test')
        cached_snap_path = snap_cache.cache(snap_filename=snap_file)

        expected_snap_path = os.path.join(
            snap_cache.snap_cache_root,
            self.deb_arch,
            file_utils.calculate_sha3_384(snap_path)
        )

        self.assertEqual(
            expected_snap_path,
            cached_snap_path
        )
        self.assertTrue(os.path.isfile(cached_snap_path))
Example #15
0
def upload(snap_filename, release_channels=None) -> Tuple[str, int]:
    """Upload a snap_filename to the store.

    If a cached snap is available, a delta will be generated from
    the cached snap to the new target snap and uploaded instead. In the
    case of a delta processing or upload failure, upload will fall back to
    uploading the full snap.

    If release_channels is defined it also releases it to those channels if the
    store deems the uploaded snap as ready to release.
    """
    snap_yaml = _get_data_from_snap_file(snap_filename)
    snap_name = snap_yaml["name"]
    built_at = snap_yaml.get("snapcraft-started-at")

    logger.debug("Run upload precheck and verify cached data for {!r}.".format(
        snap_filename))
    store_client = StoreClientCLI()
    store_client.upload_precheck(snap_name=snap_name)

    snap_cache = cache.SnapCache(project_name=snap_name)

    try:
        deb_arch = snap_yaml["architectures"][0]
    except KeyError:
        deb_arch = "all"

    source_snap = snap_cache.get(deb_arch=deb_arch)
    sha3_384_available = hasattr(hashlib, "sha3_384")

    result: Optional[Dict[str, Any]] = None
    if sha3_384_available and source_snap:
        try:
            result = _upload_delta(
                store_client,
                snap_name=snap_name,
                snap_filename=snap_filename,
                source_snap=source_snap,
                built_at=built_at,
                channels=release_channels,
            )
        except storeapi.errors.StoreDeltaApplicationError as e:
            logger.warning("Error generating delta: {}\n"
                           "Falling back to uploading full snap...".format(
                               str(e)))
        except storeapi.errors.StoreUploadError as upload_error:
            logger.warning("Unable to upload delta to store: {}\n"
                           "Falling back to uploading full snap...".format(
                               upload_error.error_list))

    if result is None:
        result = _upload_snap(
            store_client,
            snap_name=snap_name,
            snap_filename=snap_filename,
            built_at=built_at,
            channels=release_channels,
        )

    snap_cache.cache(snap_filename=snap_filename)
    snap_cache.prune(deb_arch=deb_arch,
                     keep_hash=calculate_sha3_384(snap_filename))

    return snap_name, result["revision"]
Example #16
0
    def test_prune_snap_cache(self):
        self.useFixture(fixture_setup.FakeTerminal())

        # Create snaps
        with open(os.path.join(self.path, 'snapcraft.yaml'), 'w') as f:
            f.write("""name: my-snap-name
summary: test cached snap
description: test cached snap
architectures: ['{}']
confinement: devmode
grade: devel
version: '0.1'

parts:
    my-part:
      plugin: nil
""".format(self.deb_arch))
        main(['snap'])

        snap_file = glob.glob('*0.1_*.snap')[0]

        snap_cache = cache.SnapCache(project_name='my-snap-name')
        snap_file_path = snap_cache.cache(snap_filename=snap_file)
        _, snap_file_hash = os.path.split(snap_file_path)

        with open(os.path.join(self.path, 'snapcraft.yaml'), 'w') as f:
            f.write("""name: my-snap-name
summary: test cached snap
description: test cached snap
architectures: ['{}']
confinement: devmode
grade: devel
version: '0.2'

parts:
    my-part:
      plugin: nil
""".format(self.deb_arch))

        main(['snap'])
        snap_file_2 = glob.glob('*0.2*.snap')[0]
        snap_file_2_path = snap_cache.cache(snap_filename=snap_file_2)
        snap_file_2_dir, snap_file_2_hash = os.path.split(snap_file_2_path)

        # confirm expected snap cached
        self.assertEqual(2, len(os.listdir(snap_file_2_dir)))

        # prune
        pruned_files = snap_cache.prune(deb_arch=self.deb_arch,
                                        keep_hash=snap_file_2_hash)

        self.assertEqual(1, len(pruned_files))
        self.assertIn(
            os.path.join(
                snap_cache.snap_cache_root,
                self.deb_arch,
                snap_file_hash
            ),
            pruned_files
        )
        self.assertNotIn(
            os.path.join(snap_cache.snap_cache_root, snap_file_2_hash),
            pruned_files
        )
Example #17
0
    def test_prune_snap_cache(self):
        # Create snaps
        with open(os.path.join(self.path, "snapcraft.yaml"), "w") as f:
            f.write(
                """name: my-snap-name
summary: test cached snap
description: test cached snap
architectures: ['{}']
confinement: devmode
grade: devel
version: '0.1'

parts:
    my-part:
      plugin: nil
""".format(
                    self.deb_arch
                )
            )
        result = self.run_command(["snap"])
        self.assertThat(result.exit_code, Equals(0))
        snap_file = glob.glob("*0.1_*.snap")[0]

        snap_cache = cache.SnapCache(project_name="my-snap-name")
        snap_file_path = snap_cache.cache(snap_filename=snap_file)
        _, snap_file_hash = os.path.split(snap_file_path)

        with open(os.path.join(self.path, "snapcraft.yaml"), "w") as f:
            f.write(
                """name: my-snap-name
summary: test cached snap
description: test cached snap
architectures: ['{}']
confinement: devmode
grade: devel
version: '0.2'

parts:
    my-part:
      plugin: nil
""".format(
                    self.deb_arch
                )
            )
        result = self.run_command(["snap"])
        self.assertThat(result.exit_code, Equals(0))
        snap_file_2 = glob.glob("*0.2*.snap")[0]
        snap_file_2_path = snap_cache.cache(snap_filename=snap_file_2)
        snap_file_2_dir, snap_file_2_hash = os.path.split(snap_file_2_path)

        # confirm expected snap cached
        self.assertThat(len(os.listdir(snap_file_2_dir)), Equals(2))

        # prune
        pruned_files = snap_cache.prune(
            deb_arch=self.deb_arch, keep_hash=snap_file_2_hash
        )

        self.assertThat(len(pruned_files), Equals(1))
        self.assertIn(
            os.path.join(snap_cache.snap_cache_root, self.deb_arch, snap_file_hash),
            pruned_files,
        )
        self.assertNotIn(
            os.path.join(snap_cache.snap_cache_root, snap_file_2_hash), pruned_files
        )