コード例 #1
0
ファイル: config.py プロジェクト: fuhbar/k8s-snapshots
    def read_volume(name):
        _log = _logger.new(volume_name=name, )
        env_name = name.replace('-', '_').upper()
        deltas_str = os.environ.get('VOLUME_{}_DELTAS'.format(env_name))
        if not deltas_str:
            raise ConfigError(
                'A volume {} was defined, but VOLUME_{}_DELTAS is not set'.
                format(name, env_name))

        zone = os.environ.get('VOLUME_{}_ZONE'.format(env_name))
        if not zone:
            raise ConfigError(
                'A volume {} was defined, but VOLUME_{}_ZONE is not set'.
                format(name, env_name))

        _log = _log.bind(
            deltas_str=deltas_str,
            zone=zone,
        )

        rule = Rule(
            name=name,
            claim_name='',
            namespace='',
            deltas=parse_deltas(deltas_str),
            gce_disk=name,
            gce_disk_zone=zone,
        )

        _log.info(events.Rule.ADDED_FROM_CONFIG, rule=rule)

        return rule
コード例 #2
0
 def setUp(self):
     self.mock_context = Context({})
     self.rule = Rule(name='test_rule',
                      deltas=[timedelta(hours=1),
                              timedelta(days=30)],
                      backend='test_backend',
                      disk=self.TEST_DISK)
コード例 #3
0
ファイル: core.py プロジェクト: funkypenguin/k8s-snapshots
async def rule_from_snapshotrule(
    ctx: Context,
    resource: SnapshotRule
) -> Optional[Rule]:
    """This tries to build a rule within a `SnapshotRule` resource -
    the resource that we custom designed for this purpose.

    This is invoked whenever Kubernetes tells us that such a resource
    was created, deleted, or updated.

    There are two separate ways a `SnapshotRule` can be used:

    - A `SnapshotRule` resource can refer to a specific Cloud disk
      id to be snapshotted, e.g. 'example-disk' on 'gcloud'. This
      skips Kubernetes entirely.

    - A `SnapshotRule` resource can refer to a `PersistentVolumeClaim`.
      The disk this claim is bound to is the one we will snapshot.
      Rather than defining the snapshot interval etc. as annotations
      of the claim, they are defined here, in a separate resource.
    """
    _log = _logger.new(resource=resource, rule=resource.obj)

    spec = resource.obj.get('spec', {})

    # Validate the deltas
    try:
        deltas_str = resource.obj.get('spec', {}).get('deltas')
        try:
            deltas = parse_deltas(deltas_str)
        except DeltasParseError as exc:
            raise AnnotationError(
                'Invalid delta string',
                deltas_str=deltas_str
            ) from exc

        if deltas is None or not deltas:
            raise AnnotationError(
                'parse_deltas returned invalid deltas',
                deltas_str=deltas_str,
                deltas=deltas,
            )
    except AnnotationError:
        _log.exception(
            'rule.invalid',
            key_hints=['rule.metadata.name'],
        )
        return

    # Refers to a disk from a cloud provider
    if spec.get('disk'):
        # Validate the backend
        backend_name = spec.get('backend')
        try:
            backend = get_backend(backend_name)
        except ConfigurationError as e:
            _log.exception(
                'rule.invalid',
                message=e.message,
                backend=backend_name
            )
            return

        # Validate the disk identifier
        disk = resource.obj.get('spec', {}).get('disk')
        try:
            disk = backend.validate_disk_identifier(disk)
        except ValueError:
            _log.exception(
                'rule.invalid',
                key_hints=['rule.metadata.name'],
            )
            return

        rule = Rule(
            name=rule_name_from_k8s_source(resource),
            deltas=deltas,
            backend=backend_name,
            disk=disk
        )
        return rule

    # Refers to a volume claim
    if spec.get('persistentVolumeClaim'):

        # Find the claim
        volume_claim = await get_resource_or_none(
            ctx.kube_client,
            pykube.objects.PersistentVolumeClaim,
            spec.get('persistentVolumeClaim'),
            namespace=resource.metadata['namespace']
        )

        if not volume_claim:
            _log.warning(
                events.Rule.PENDING,
                reason='Volume claim does not exist',
                key_hints=['rule.metadata.name'],
            )
            raise RuleDependsOn(
                'The volume claim targeted by this SnapshotRule does not exist yet',
                kind='PersistentVolumeClaim',
                namespace=resource.metadata['namespace'],
                name=spec.get('persistentVolumeClaim')
            )

        # Find the volume
        try:
            volume = await volume_from_pvc(ctx, volume_claim)
        except VolumeNotFound:
            _log.warning(
                events.Rule.PENDING,
                reason='Volume claim is not bound',
                key_hints=['rule.metadata.name'],
            )
            raise RuleDependsOn(
                'The volume claim targeted by this SnapshotRule is not bound yet',
                kind='PersistentVolumeClaim',
                namespace=resource.metadata['namespace'],
                name=spec.get('persistentVolumeClaim')
            )

        return await rule_from_pv(ctx, volume, deltas, source=resource)
コード例 #4
0
ファイル: core.py プロジェクト: yawboateng/k8s-snapshots
async def rule_from_resource(
    ctx: Context, resource: Union[pykube.objects.PersistentVolume,
                                  pykube.objects.PersistentVolumeClaim,
                                  SnapshotRule]
) -> Rule:
    """Given a Kubernetes resource, converts it to a snapshot `Rule`
    instance, or returns None.

    How this process works, depends on the resource given.

    - If the resource is a volume, we read the disk and delta info
      from there.

    - If the resource is a volume claim, we look for the volume.

    - A `SnapshotRule` custom resource directly defines the disk.
    """

    _log = _logger.new(resource=resource, )

    if (isinstance(resource, SnapshotRule)):
        # Validate the backen
        backend_name = resource.obj.get('spec', {}).get('backend')
        try:
            backend = get_backend(backend_name)
        except ConfigurationError as e:
            _log.exception('rule.invalid',
                           message=e.message,
                           backend=backend_name)
            return

        # Validate the deltas
        try:
            deltas_str = resource.obj.get('spec', {}).get('deltas')
            try:
                deltas = parse_deltas(deltas_str)
            except DeltasParseError as exc:
                raise AnnotationError('Invalid delta string',
                                      deltas_str=deltas_str) from exc

            if deltas is None or not deltas:
                raise AnnotationError(
                    'parse_deltas returned invalid deltas',
                    deltas_str=deltas_str,
                    deltas=deltas,
                )
        except AnnotationError:
            _log.exception(
                'rule.invalid',
                key_hints=['volume.metadata.name'],
            )
            return

        # Validate the disk identifier
        disk = resource.obj.get('spec', {}).get('disk')
        try:
            disk = backend.validate_disk_identifier(disk)
        except ValueError:
            _log.exception(
                'rule.invalid',
                key_hints=['volume.metadata.name'],
            )
            return

        rule = Rule(name=rule_name_from_k8s_source(resource),
                    deltas=deltas,
                    backend=backend_name,
                    disk=disk)
        return rule

    if isinstance(resource, pykube.objects.PersistentVolumeClaim):
        try:
            volume = await volume_from_pvc(ctx, resource)
        except VolumeNotFound:
            _log.exception(
                events.Volume.NOT_FOUND,
                key_hints=[
                    'resource.metadata.name',
                ],
            )
            return

    elif isinstance(resource, pykube.objects.PersistentVolume):
        volume = resource

    else:
        raise RuntimeError(f'{resource} is not supported.')

    volume_name = volume.name
    _log = _log.bind(
        volume_name=volume_name,
        volume=volume.obj,
    )

    try:
        rule = await rule_from_pv(
            ctx,
            volume,
            ctx.config.get('deltas_annotation_key'),
            use_claim_name=ctx.config.get('use_claim_name'))
        return rule
    except AnnotationNotFound as exc:
        _log.info(
            events.Annotation.NOT_FOUND,
            key_hints=['volume.metadata.name'],
            exc_info=exc,
        )
    except AnnotationError:
        _log.exception(
            events.Annotation.ERROR,
            key_hints=['volume.metadata.name'],
        )
    except UnsupportedVolume as exc:
        _log.info(
            events.Volume.UNSUPPORTED,
            key_hints=['volume.metadata.name'],
            exc_info=exc,
        )