def test_database_update_package(fake_db):
    test_package = fake_db.get_package('test-package-2')
    test_package.installed = True
    test_package.version = Version(1, 2, 3)
    fake_db.update_package(test_package)
    test_package = fake_db.get_package('test-package-2')
    assert test_package.installed
    assert test_package.version == Version(1, 2, 3)
Example #2
0
def test_constraint_multiple():
    package_constraint = PackageConstraint.parse('swss>1.2.0,<3.0.0,!=2.2.2')
    assert package_constraint.name == 'swss'
    assert not package_constraint.constraint.allows(Version.parse('2.2.2'))
    assert not package_constraint.constraint.allows(Version.parse('3.2.0'))
    assert not package_constraint.constraint.allows(Version.parse('0.2.0'))
    assert package_constraint.constraint.allows(Version.parse('2.2.3'))
    assert package_constraint.constraint.allows(Version.parse('1.2.3'))
Example #3
0
 def __init__(self):
     self.metadata_store = {}
     self.add('docker-database', 'latest', 'database', '1.0.0')
     self.add(
         'docker-orchagent',
         'latest',
         'swss',
         '1.0.0',
         components={
             'libswsscommon': Version.parse('1.0.0'),
             'libsairedis': Version.parse('1.0.0')
         },
         warm_shutdown={
             'before': ['syncd'],
         },
         fast_shutdown={
             'before': ['syncd'],
         },
         processes=[{
             'name': 'orchagent',
             'reconciles': True,
         }, {
             'name': 'neighsyncd',
             'reconciles': True,
         }],
     )
     self.add('docker-syncd', 'latest', 'syncd', '1.0.0')
     self.add('docker-teamd',
              'latest',
              'teamd',
              '1.0.0',
              components={
                  'libswsscommon': Version.parse('1.0.0'),
                  'libsairedis': Version.parse('1.0.0')
              },
              warm_shutdown={
                  'before': ['syncd'],
                  'after': ['swss'],
              },
              fast_shutdown={
                  'before': ['swss'],
              })
     self.add('Azure/docker-test', '1.6.0', 'test-package', '1.6.0')
     self.add('Azure/docker-test-2', '1.5.0', 'test-package-2', '1.5.0')
     self.add('Azure/docker-test-2', '2.0.0', 'test-package-2', '2.0.0')
     self.add('Azure/docker-test-3', 'latest', 'test-package-3',
              '1.6.0')
     self.add('Azure/docker-test-3', '1.5.0', 'test-package-3', '1.5.0')
     self.add('Azure/docker-test-3', '1.6.0', 'test-package-3', '1.6.0')
     self.add('Azure/docker-test-4', '1.5.0', 'test-package-4', '1.5.0')
     self.add('Azure/docker-test-5', '1.5.0', 'test-package-5', '1.5.0')
     self.add('Azure/docker-test-5', '1.9.0', 'test-package-5', '1.9.0')
     self.add('Azure/docker-test-6', '1.5.0', 'test-package-6', '1.5.0')
     self.add('Azure/docker-test-6', '1.9.0', 'test-package-6', '1.9.0')
     self.add('Azure/docker-test-6', '2.0.0', 'test-package-6', '2.0.0')
     self.add('Azure/docker-test-6', 'latest', 'test-package-6',
              '1.5.0')
def test_database_get_package(fake_db):
    swss_package = fake_db.get_package('swss')
    assert swss_package.installed
    assert swss_package.built_in
    assert swss_package.repository == 'docker-orchagent'
    assert swss_package.default_reference == '1.0.0'
    assert swss_package.version == Version(1, 0, 0)
Example #5
0
def test_manager_package_reset(package_manager, sonic_fs):
    package_manager.install('test-package-6=1.5.0')
    package_manager.install('test-package-6=2.0.0')

    package_manager.reset('test-package-6')
    upgraded_package = package_manager.get_installed_package('test-package-6')
    assert upgraded_package.entry.version == Version(1, 5, 0)
Example #6
0
def test_constraint_from_dict():
    package_constraint = PackageConstraint.parse({
        'name': 'swss',
        'version': '^1.0.0',
        'components': {
            'libswsscommon': '^1.1.0',
        },
    })
    assert package_constraint.name == 'swss'
    assert package_constraint.constraint.allows(Version.parse('1.0.0'))
    assert not package_constraint.constraint.allows(Version.parse('2.0.0'))
    assert package_constraint.components['libswsscommon'].allows(
        Version.parse('1.2.0'))
    assert not package_constraint.components['libswsscommon'].allows(
        Version.parse('1.0.0'))
    assert not package_constraint.components['libswsscommon'].allows(
        Version.parse('2.0.0'))
Example #7
0
def test_manager_upgrade(package_manager, sonic_fs):
    package_manager.install('test-package-6=1.5.0')
    package = package_manager.get_installed_package('test-package-6')

    package_manager.install('test-package-6=2.0.0')
    upgraded_package = package_manager.get_installed_package('test-package-6')
    assert upgraded_package.entry.version == Version(2, 0, 0)
    assert upgraded_package.entry.default_reference == package.entry.default_reference
Example #8
0
 def __init__(self):
     self.metadata_store = {}
     self.add('docker-database', 'latest', 'database', '1.0.0')
     self.add('docker-orchagent', 'latest', 'swss', '1.0.0',
              components={
                  'libswsscommon': Version.parse('1.0.0'),
                  'libsairedis': Version.parse('1.0.0')
              }
     )
     self.add('Azure/docker-test', '1.6.0', 'test-package', '1.6.0')
     self.add('Azure/docker-test-2', '1.5.0', 'test-package-2', '1.5.0')
     self.add('Azure/docker-test-2', '2.0.0', 'test-package-2', '2.0.0')
     self.add('Azure/docker-test-3', 'latest', 'test-package-3', '1.6.0')
     self.add('Azure/docker-test-3', '1.5.0', 'test-package-3', '1.5.0')
     self.add('Azure/docker-test-3', '1.6.0', 'test-package-3', '1.6.0')
     self.add('Azure/docker-test-4', '1.5.0', 'test-package-4', '1.5.0')
     self.add('Azure/docker-test-5', '1.5.0', 'test-package-5', '1.5.0')
     self.add('Azure/docker-test-5', '1.9.0', 'test-package-5', '1.9.0')
     self.add('Azure/docker-test-6', '1.5.0', 'test-package-6', '1.5.0')
     self.add('Azure/docker-test-6', '1.9.0', 'test-package-6', '1.9.0')
     self.add('Azure/docker-test-6', '2.0.0', 'test-package-6', '2.0.0')
     self.add('Azure/docker-test-6', 'latest', 'test-package-6', '1.5.0')
Example #9
0
def package_from_dict(name: str, package_info: Dict) -> PackageEntry:
    """ Parse dictionary into PackageEntry object."""

    repository = package_info.get('repository')
    description = package_info.get('description')
    default_reference = package_info.get('default-reference')
    version = package_info.get('installed-version')
    if version:
        version = Version.parse(version)
    installed = package_info.get('installed', False)
    built_in = package_info.get('built-in', False)
    image_id = package_info.get('image-id')

    return PackageEntry(name, repository, description, default_reference,
                        version, installed, built_in, image_id)
Example #10
0
def test_installation_components_dependencies_satisfied(
        package_manager, fake_metadata_resolver):
    metadata = fake_metadata_resolver.metadata_store['Azure/docker-test'][
        '1.6.0']
    manifest = metadata['manifest']
    metadata['components'] = {'libswsscommon': Version.parse('1.1.0')}
    manifest['package']['depends'] = [
        {
            'name': 'swss',
            'version': '>=1.0.0',
            'components': {
                'libswsscommon': '^1.0.0',
            },
        },
    ]
    package_manager.install('test-package')
Example #11
0
def test_installation_components_dependencies_implicit(package_manager,
                                                       fake_metadata_resolver):
    metadata = fake_metadata_resolver.metadata_store['Azure/docker-test'][
        '1.6.0']
    manifest = metadata['manifest']
    metadata['components'] = {'libswsscommon': Version.parse('2.1.0')}
    manifest['package']['depends'] = [
        {
            'name': 'swss',
            'version': '>=1.0.0',
        },
    ]
    with pytest.raises(
            PackageInstallationError,
            match='Package test-package requires libswsscommon >=2.1.0,<3.0.0 '
            'in package swss>=1.0.0 but version 1.0.0 is installed'):
        package_manager.install('test-package')
Example #12
0
def validate_package_base_os_constraints(package: Package, sonic_version_info: Dict[str, str]):
    """ Verify that all dependencies on base OS components are met.
    Args:
        package: Package to check constraints for.
        sonic_version_info: SONiC components version information.
    Raises:
        PackageSonicRequirementError: in case dependency is not satisfied.
    """

    base_os_constraints = package.manifest['package']['base-os'].components
    for component, constraint in base_os_constraints.items():
        if component not in sonic_version_info:
            raise PackageSonicRequirementError(package.name, component, constraint)

        version = Version.parse(sonic_version_info[component])

        if not constraint.allows_all(version):
            raise PackageSonicRequirementError(package.name, component, constraint, version)
Example #13
0
    def from_labels(cls, labels: Dict[str, str]) -> Metadata:
        """ Get manifest from image labels.

        Args:
            labels: key, value string pairs
        Returns:
            Metadata
        Raises:
            MetadataError
        """

        metadata_dict = translate_plain_to_tree(labels)
        try:
            sonic_metadata = metadata_dict['com']['azure']['sonic']
        except KeyError:
            raise MetadataError('No metadata found in image labels')

        try:
            manifest_string = sonic_metadata['manifest']
        except KeyError:
            raise MetadataError('No manifest found in image labels')

        try:
            manifest_dict = json.loads(manifest_string)
        except (ValueError, TypeError) as err:
            raise MetadataError(f'Failed to parse manifest JSON: {err}')

        components = {}
        if 'versions' in sonic_metadata:
            for component, version in sonic_metadata['versions'].items():
                try:
                    components[component] = Version.parse(version)
                except ValueError as err:
                    raise MetadataError(
                        f'Failed to parse component version: {err}')

        yang_module_str = sonic_metadata.get('yang-module')

        return Metadata(Manifest.marshal(manifest_dict), components,
                        yang_module_str)
Example #14
0
def test_invalid_version(invalid_version):
    with pytest.raises(Exception):
        Version.parse(invalid_version)
Example #15
0
def test_version_to_tag():
    assert version.version_to_tag(Version.parse('1.0.0-rc0')) == '1.0.0-rc0'
    assert version.version_to_tag(
        Version.parse('1.0.0-rc0+152')) == '1.0.0-rc0_152'
Example #16
0
class ManifestSchema:
    """ ManifestSchema class describes and provides marshalling
    and unmarshalling methods.
    """

    class Marshaller:
        """ Base class for marshaling and un-marshaling. """

        def marshal(self, value):
            """ Validates and returns a valid manifest dictionary.

            Args:
                value: input value to validate.
            Returns: valid manifest node.
            """

            raise NotImplementedError

        def unmarshal(self, value):
            """ Un-marshals the manifest to a dictionary.

            Args:
                value: input value to validate.
            Returns: valid manifest node.
            """

            raise NotImplementedError

    @dataclass
    class ParsedMarshaller(Marshaller):
        """ Marshaller used on types which support class method "parse" """

        type: Any

        def marshal(self, value):
            try:
                return self.type.parse(value)
            except ValueError as err:
                raise ManifestError(f'Failed to marshal {value}: {err}')

        def unmarshal(self, value):
            try:
                if hasattr(value, 'deparse'):
                    return value.deparse()
                return str(value)
            except Exception as err:
                raise ManifestError(f'Failed to unmarshal {value}: {err}')

    @dataclass
    class DefaultMarshaller(Marshaller):
        """ Default marshaller that validates if the given
        value is instance of given type. """

        type: type

        def marshal(self, value):
            if not isinstance(value, self.type):
                raise ManifestError(f'{value} is not of type {self.type.__name__}')
            return value

        def unmarshal(self, value):
            return value

    @dataclass
    class ManifestNode(Marshaller, ABC):
        """
        Base class for any manifest object.

        Attrs:
            key: String representing the key for this object.
        """

        key: str

    @dataclass
    class ManifestRoot(ManifestNode):
        items: List

        def marshal(self, value: Optional[dict]):
            result = {}
            value = value or {}

            if not isinstance(value, dict):
                raise ManifestError(f'"{self.key}" field has to be a dictionary')

            for item in self.items:
                next_value = value.get(item.key)
                result[item.key] = item.marshal(next_value)
            return result

        def unmarshal(self, value):
            return_value = {}
            for item in self.items:
                return_value[item.key] = item.unmarshal(value[item.key])
            return return_value

    @dataclass
    class ManifestField(ManifestNode):
        type: Any
        default: Optional[Any] = None

        def marshal(self, value):
            if value is None:
                if self.default is not None:
                    return self.default
                raise ManifestError(f'"{self.key}" is a required field but it is missing')
            try:
                return_value = self.type.marshal(value)
            except Exception as err:
                raise ManifestError(f'Failed to marshal {self.key}: {err}')
            return return_value

        def unmarshal(self, value):
            return self.type.unmarshal(value)

    @dataclass
    class ManifestArray(ManifestNode):
        type: Any

        def marshal(self, value):
            return_value = []
            value = value or []

            if not isinstance(value, list):
                raise ManifestError(f'"{self.key}" has to be of type list')

            try:
                for item in value:
                    return_value.append(self.type.marshal(item))
            except Exception as err:
                raise ManifestError(f'Failed to convert {self.key}={value} to array: {err}')

            return return_value

        def unmarshal(self, value):
            return [self.type.unmarshal(item) for item in value]

    # TODO: add description for each field
    SCHEMA = ManifestRoot('root', [
        ManifestField('version', ParsedMarshaller(Version), Version.parse('1.0.0')),
        ManifestRoot('package', [
            ManifestField('version', ParsedMarshaller(Version)),
            ManifestField('name', DefaultMarshaller(str)),
            ManifestField('description', DefaultMarshaller(str), ''),
            ManifestField('base-os', ParsedMarshaller(ComponentConstraints), ComponentConstraints()),
            ManifestArray('depends', ParsedMarshaller(PackageConstraint)),
            ManifestArray('breaks', ParsedMarshaller(PackageConstraint)),
            ManifestField('init-cfg', DefaultMarshaller(dict), dict()),
            ManifestField('changelog', DefaultMarshaller(dict), dict()),
            ManifestField('debug-dump', DefaultMarshaller(str), ''),
        ]),
        ManifestRoot('service', [
            ManifestField('name', DefaultMarshaller(str)),
            ManifestArray('requires', DefaultMarshaller(str)),
            ManifestArray('requisite', DefaultMarshaller(str)),
            ManifestArray('wanted-by', DefaultMarshaller(str)),
            ManifestArray('after', DefaultMarshaller(str)),
            ManifestArray('before', DefaultMarshaller(str)),
            ManifestArray('dependent', DefaultMarshaller(str)),
            ManifestArray('dependent-of', DefaultMarshaller(str)),
            ManifestField('post-start-action', DefaultMarshaller(str), ''),
            ManifestField('pre-shutdown-action', DefaultMarshaller(str), ''),
            ManifestField('asic-service', DefaultMarshaller(bool), False),
            ManifestField('host-service', DefaultMarshaller(bool), True),
            ManifestField('delayed', DefaultMarshaller(bool), False),
            ManifestRoot('warm-shutdown', [
                ManifestArray('after', DefaultMarshaller(str)),
                ManifestArray('before', DefaultMarshaller(str)),
            ]),
            ManifestRoot('fast-shutdown', [
                ManifestArray('after', DefaultMarshaller(str)),
                ManifestArray('before', DefaultMarshaller(str)),
            ]),
        ]),
        ManifestRoot('container', [
            ManifestField('privileged', DefaultMarshaller(bool), False),
            ManifestArray('volumes', DefaultMarshaller(str)),
            ManifestArray('mounts', ManifestRoot('mounts', [
                ManifestField('source', DefaultMarshaller(str)),
                ManifestField('target', DefaultMarshaller(str)),
                ManifestField('type', DefaultMarshaller(str)),
            ])),
            ManifestField('environment', DefaultMarshaller(dict), dict()),
            ManifestArray('tmpfs', DefaultMarshaller(str)),
        ]),
        ManifestArray('processes', ManifestRoot('processes', [
            ManifestField('name', DefaultMarshaller(str)),
            ManifestField('reconciles', DefaultMarshaller(bool), False),
        ])),
        ManifestRoot('cli', [
            ManifestField('mandatory', DefaultMarshaller(bool), False),
            ManifestField('show', DefaultMarshaller(str), ''),
            ManifestField('config', DefaultMarshaller(str), ''),
            ManifestField('clear', DefaultMarshaller(str), '')
        ])
    ])
Example #17
0
def test_version_comparison(newer, older):
    assert Version.parse(newer) > Version.parse(older)
Example #18
0
def test_constraint():
    package_constraint = PackageConstraint.parse('swss>1.0.0')
    assert package_constraint.name == 'swss'
    assert not package_constraint.constraint.allows(Version.parse('0.9.1'))
    assert package_constraint.constraint.allows(Version.parse('1.1.1'))
Example #19
0
def test_constraint_range():
    package_constraint = PackageConstraint.parse('swss^1.2.0')
    assert package_constraint.name == 'swss'
    assert not package_constraint.constraint.allows(Version.parse('1.1.1'))
    assert package_constraint.constraint.allows(Version.parse('1.2.5'))
    assert not package_constraint.constraint.allows(Version.parse('2.0.1'))
Example #20
0
def test_constraint_match():
    package_constraint = PackageConstraint.parse('swss==1.2.*')
    assert package_constraint.name == 'swss'
    assert not package_constraint.constraint.allows(Version.parse('1.1.1'))
    assert package_constraint.constraint.allows(Version.parse('1.2.0'))