コード例 #1
0
    def _read_and_clear_state(self):
        if self.CHARM_STATE_FILE.stat().st_size:
            storage = SQLiteStorage(self.CHARM_STATE_FILE)
            with (self.JUJU_CHARM_DIR / 'metadata.yaml').open() as m:
                af = (self.JUJU_CHARM_DIR / 'actions.yaml')
                if af.exists():
                    with af.open() as a:
                        meta = CharmMeta.from_yaml(m, a)
                else:
                    meta = CharmMeta.from_yaml(m)
            framework = Framework(storage, self.JUJU_CHARM_DIR, meta, None)

            class ThisCharmEvents(CharmEvents):
                pass

            class Charm(self.charm_module.Charm):
                on = ThisCharmEvents()

            mycharm = Charm(framework)
            stored = mycharm._stored
            # Override the saved data with a cleared state
            storage.save_snapshot(stored._data.handle.path, {})
            storage.commit()
            framework.close()
        else:
            stored = StoredStateData(None, None)
        return stored
コード例 #2
0
ファイル: test_charm.py プロジェクト: ducks23/charm-slurmd
    def setUp(self):
        def restore_env(env):
            os.environ.clear()
            os.environ.update(env)

        self.addCleanup(restore_env, os.environ.copy())

        os.environ['PATH'] = "{}:{}".format(
            Path(__file__).parent / 'bin', os.environ['PATH'])
        os.environ['JUJU_UNIT_NAME'] = 'local/0'

        self.tmpdir = Path(tempfile.mkdtemp())
        self.addCleanup(shutil.rmtree, str(self.tmpdir))
        self.meta = CharmMeta()

        class CustomEvent(EventBase):
            pass

        class TestCharmEvents(CharmEvents):
            custom = EventSource(CustomEvent)

        # Relations events are defined dynamically and modify the class attributes.
        # We use a subclass temporarily to prevent these side effects from leaking.
        CharmBase.on = TestCharmEvents()

        def cleanup():
            CharmBase.on = CharmEvents()

        self.addCleanup(cleanup)
コード例 #3
0
    def setUp(self):
        self._path = os.environ['PATH']
        os.environ['PATH'] = str(Path(__file__).parent / 'bin')

        os.environ['JUJU_UNIT_NAME'] = 'local/0'

        self.tmpdir = Path(tempfile.mkdtemp())
        self.addCleanup(shutil.rmtree, self.tmpdir)
        self.meta = CharmMeta()

        class CustomEvent(EventBase):
            pass

        class TestCharmEvents(CharmEvents):
            custom = EventSource(CustomEvent)

        # Relations events are defined dynamically and modify the class attributes.
        # We use a subclass temporarily to prevent these side effects from leaking.
        CharmBase.on = TestCharmEvents()

        def cleanup():
            os.environ['PATH'] = self._path
            del os.environ['JUJU_UNIT_NAME']
            CharmBase.on = CharmEvents()
        self.addCleanup(cleanup)
コード例 #4
0
ファイル: test_charm.py プロジェクト: timClicks/gitea-charm
    def test_relation_events(self):

        class MyCharm(CharmBase):
            def __init__(self, *args):
                super().__init__(*args)
                self.seen = []
                for rel in ('req1', 'req-2', 'pro1', 'pro-2', 'peer1', 'peer-2'):
                    # Hook up relation events to generic handler.
                    self.framework.observe(self.on[rel].relation_joined, self.on_any_relation)
                    self.framework.observe(self.on[rel].relation_changed, self.on_any_relation)
                    self.framework.observe(self.on[rel].relation_departed, self.on_any_relation)
                    self.framework.observe(self.on[rel].relation_broken, self.on_any_relation)

            def on_any_relation(self, event):
                assert event.relation.name == 'req1'
                assert event.relation.app.name == 'remote'
                self.seen.append(type(event).__name__)

        # language=YAML
        self.meta = CharmMeta.from_yaml(metadata='''
name: my-charm
requires:
 req1:
   interface: req1
 req-2:
   interface: req2
provides:
 pro1:
   interface: pro1
 pro-2:
   interface: pro2
peers:
 peer1:
   interface: peer1
 peer-2:
   interface: peer2
''')

        charm = MyCharm(self.create_framework(), None)

        rel = charm.framework.model.get_relation('req1', 1)
        unit = charm.framework.model.get_unit('remote/0')
        charm.on['req1'].relation_joined.emit(rel, unit)
        charm.on['req1'].relation_changed.emit(rel, unit)
        charm.on['req-2'].relation_changed.emit(rel, unit)
        charm.on['pro1'].relation_departed.emit(rel, unit)
        charm.on['pro-2'].relation_departed.emit(rel, unit)
        charm.on['peer1'].relation_broken.emit(rel)
        charm.on['peer-2'].relation_broken.emit(rel)

        self.assertEqual(charm.seen, [
            'RelationJoinedEvent',
            'RelationChangedEvent',
            'RelationChangedEvent',
            'RelationDepartedEvent',
            'RelationDepartedEvent',
            'RelationBrokenEvent',
            'RelationBrokenEvent',
        ])
コード例 #5
0
    def create_framework(self):
        framework = Framework(self.tmpdir / "framework.data", self.tmpdir,
                              CharmMeta(), None)
        # Ensure that the Framework object is closed and cleaned up even
        # when the test fails or errors out.
        self.addCleanup(framework.close)

        return framework
コード例 #6
0
ファイル: test_main.py プロジェクト: phvalguima/operator
 def test_not_if_already_local(self):
     meta = CharmMeta.from_yaml("series: [kubernetes]")
     with patch.dict(
             os.environ,
         {"JUJU_VERSION": "2.8"}), tempfile.NamedTemporaryFile() as fd:
         self.assertFalse(
             _should_use_controller_storage(Path(fd.name), meta))
         self.assertLogged('Using local storage: {} already exists'.format(
             fd.name))
コード例 #7
0
    def test_relations_meta_limit_type_validation(self):
        with self.assertRaisesRegex(TypeError, "limit should be an int, not <class 'str'>"):
            # language=YAML
            self.meta = CharmMeta.from_yaml('''
name: my-charm
requires:
  database:
    interface: mongodb
    limit: foobar
''')
コード例 #8
0
ファイル: test_helpers.py プロジェクト: xiryrg1/operator
    def create_model(self):
        """Create a Model object."""
        unit_name = 'myapp/0'
        patcher = patch.dict(os.environ, {'JUJU_UNIT_NAME': unit_name})
        patcher.start()
        self.addCleanup(patcher.stop)

        backend = _ModelBackend()
        meta = CharmMeta()
        model = Model('myapp/0', meta, backend)
        return model
コード例 #9
0
    def test_relations_meta_scope_type_validation(self):
        with self.assertRaisesRegex(TypeError,
                                    "scope should be one of 'global', 'container'; not 'foobar'"):
            # language=YAML
            self.meta = CharmMeta.from_yaml('''
name: my-charm
requires:
  database:
    interface: mongodb
    scope: foobar
''')
コード例 #10
0
ファイル: test_charm.py プロジェクト: phvalguima/operator
    def test_containers(self):
        meta = CharmMeta.from_yaml("""
name: k8s-charm
containers:
  test1:
    k: v
  test2:
    k: v
""")
        self.assertIsInstance(meta.containers['test1'], ContainerMeta)
        self.assertIsInstance(meta.containers['test2'], ContainerMeta)
        self.assertEqual(meta.containers['test1'].name, 'test1')
        self.assertEqual(meta.containers['test2'].name, 'test2')
コード例 #11
0
 class MRRMTestCharm(CharmBase):
     __name__ = self.app_name
     on = MRRMTestEvents()
     meta = CharmMeta({
         self.ROLE: {
             self._default_endpoint: {
                 "role": self.ROLE,
                 "interface": self.INTERFACE,
                 "limit": self.LIMIT,
             },
         },
     })
     app = harness.model.get_app(self.app_name)
     unit = harness.model.get_unit(self.unit_name)
コード例 #12
0
    def create_framework(self):
        model = create_autospec(Model)
        model.unit = create_autospec(Unit)
        model.unit.is_leader = MagicMock(return_value=False)
        model.app = create_autospec(Application)
        model.pod = create_autospec(Pod)
        model.config = create_autospec(ConfigData)
        raw_meta = {'provides': {'mongo': {"interface": "mongodb"}}}
        framework = Framework(
            self.tmpdir / "framework.data.{}".format(str(uuid4)), self.tmpdir,
            CharmMeta(raw=raw_meta), model)

        framework.model.app.name = "test-app"
        self.addCleanup(framework.close)

        return framework
コード例 #13
0
ファイル: test_charm.py プロジェクト: phvalguima/operator
    def _get_action_test_meta(cls):
        # language=YAML
        return CharmMeta.from_yaml(metadata='''
name: my-charm
''', actions='''
foo-bar:
  description: "Foos the bar."
  params:
    foo-name:
      description: "A foo name to bar"
      type: string
    silent:
      default: false
      description: ""
      type: boolean
  required: foo-bar
  title: foo-bar
start:
  description: "Start the unit."
''')
コード例 #14
0
    def test_relations_meta(self):

        # language=YAML
        self.meta = CharmMeta.from_yaml('''
name: my-charm
requires:
  database:
    interface: mongodb
    limit: 1
    scope: container
  metrics:
    interface: prometheus-scraping
''')

        self.assertEqual(self.meta.requires['database'].interface_name, 'mongodb')
        self.assertEqual(self.meta.requires['database'].limit, 1)
        self.assertEqual(self.meta.requires['database'].scope, 'container')

        self.assertEqual(self.meta.requires['metrics'].interface_name, 'prometheus-scraping')
        self.assertIsNone(self.meta.requires['metrics'].limit)
        self.assertEqual(self.meta.requires['metrics'].scope, 'global')  # Default value
コード例 #15
0
ファイル: test_charm.py プロジェクト: phvalguima/operator
    def test_workload_events(self):

        class MyCharm(CharmBase):
            def __init__(self, *args):
                super().__init__(*args)
                self.seen = []
                self.count = 0
                for workload in ('container-a', 'containerb'):
                    # Hook up relation events to generic handler.
                    self.framework.observe(
                        self.on[workload].pebble_ready,
                        self.on_any_pebble_ready)

            def on_any_pebble_ready(self, event):
                self.seen.append(type(event).__name__)
                self.count += 1

        # language=YAML
        self.meta = CharmMeta.from_yaml(metadata='''
name: my-charm
containers:
  container-a:
  containerb:
''')

        charm = MyCharm(self.create_framework())

        self.assertIn('container_a_pebble_ready', repr(charm.on))
        self.assertIn('containerb_pebble_ready', repr(charm.on))

        charm.on['container-a'].pebble_ready.emit(
            charm.framework.model.unit.get_container('container-a'))
        charm.on['containerb'].pebble_ready.emit(
            charm.framework.model.unit.get_container('containerb'))

        self.assertEqual(charm.seen, [
            'PebbleReadyEvent',
            'PebbleReadyEvent'
        ])
        self.assertEqual(charm.count, 2)
コード例 #16
0
    def test_containers_storage(self):
        meta = CharmMeta.from_yaml("""
name: k8s-charm
storage:
  data:
    type: filesystem
    location: /test/storage
  other:
    type: filesystem
    location: /test/other
containers:
  test1:
    mounts:
      - storage: data
        location: /test/storagemount
      - storage: other
        location: /test/otherdata
""")
        self.assertIsInstance(meta.containers['test1'], ContainerMeta)
        self.assertIsInstance(meta.containers['test1'].mounts["data"], ContainerStorageMeta)
        self.assertEqual(meta.containers['test1'].mounts["data"].location, '/test/storagemount')
        self.assertEqual(meta.containers['test1'].mounts["other"].location, '/test/otherdata')
コード例 #17
0
 def _get_function_test_meta(cls):
     return CharmMeta({'name': 'my-charm'}, {
         'foo-bar': {
             'description': 'Foos the bar.',
             'title': 'foo-bar',
             'required': 'foo-bar',
             'params': {
                 'foo-name': {
                     'type': 'string',
                     'description': 'A foo name to bar',
                 },
                 'silent': {
                     'type': 'boolean',
                     'description': '',
                     'default': False,
                 },
             },
         },
         'start': {
             'description': 'Start the unit.'
         }
     })
コード例 #18
0
    def test_containers_storage_multiple_mounts(self):
        meta = CharmMeta.from_yaml("""
name: k8s-charm
storage:
  data:
    type: filesystem
    location: /test/storage
containers:
  test1:
    mounts:
      - storage: data
        location: /test/storagemount
      - storage: data
        location: /test/otherdata
""")
        self.assertIsInstance(meta.containers['test1'], ContainerMeta)
        self.assertIsInstance(meta.containers['test1'].mounts["data"], ContainerStorageMeta)
        self.assertEqual(
            meta.containers['test1'].mounts["data"].locations[0],
            '/test/storagemount')
        self.assertEqual(meta.containers['test1'].mounts["data"].locations[1], '/test/otherdata')

        with self.assertRaises(RuntimeError):
            meta.containers["test1"].mounts["data"].location
コード例 #19
0
ファイル: test_main.py プロジェクト: zzehring/operator
 def test_fallback_to_current_juju_version__new_enough(self):
     meta = CharmMeta.from_yaml("series: [kubernetes]")
     with patch.dict(os.environ, {"JUJU_VERSION": "2.8"}):
         self.assertTrue(_should_use_controller_storage(Path("/xyzzy"), meta))
         self.assertLogged('Using controller storage: JUJU_VERSION=2.8.0')
コード例 #20
0
ファイル: test_charm.py プロジェクト: ducks23/charm-slurmd
    def test_storage_events(self):
        class MyCharm(CharmBase):
            def __init__(self, *args):
                super().__init__(*args)
                self.seen = []
                self.framework.observe(self.on['stor1'].storage_attached,
                                       self._on_stor1_attach)
                self.framework.observe(self.on['stor2'].storage_detaching,
                                       self._on_stor2_detach)
                self.framework.observe(self.on['stor3'].storage_attached,
                                       self._on_stor3_attach)
                self.framework.observe(self.on['stor-4'].storage_attached,
                                       self._on_stor4_attach)

            def _on_stor1_attach(self, event):
                self.seen.append(type(event).__name__)

            def _on_stor2_detach(self, event):
                self.seen.append(type(event).__name__)

            def _on_stor3_attach(self, event):
                self.seen.append(type(event).__name__)

            def _on_stor4_attach(self, event):
                self.seen.append(type(event).__name__)

        # language=YAML
        self.meta = CharmMeta.from_yaml('''
name: my-charm
storage:
  stor-4:
    multiple:
      range: 2-4
    type: filesystem
  stor1:
    type: filesystem
  stor2:
    multiple:
      range: "2"
    type: filesystem
  stor3:
    multiple:
      range: 2-
    type: filesystem
''')

        self.assertIsNone(self.meta.storages['stor1'].multiple_range)
        self.assertEqual(self.meta.storages['stor2'].multiple_range, (2, 2))
        self.assertEqual(self.meta.storages['stor3'].multiple_range, (2, None))
        self.assertEqual(self.meta.storages['stor-4'].multiple_range, (2, 4))

        charm = MyCharm(self.create_framework())

        charm.on['stor1'].storage_attached.emit()
        charm.on['stor2'].storage_detaching.emit()
        charm.on['stor3'].storage_attached.emit()
        charm.on['stor-4'].storage_attached.emit()

        self.assertEqual(charm.seen, [
            'StorageAttachedEvent',
            'StorageDetachingEvent',
            'StorageAttachedEvent',
            'StorageAttachedEvent',
        ])
コード例 #21
0
 def create_model(self):
     """Create a Model object."""
     backend = _ModelBackend(unit_name='myapp/0')
     meta = CharmMeta()
     model = Model(meta, backend)
     return model
コード例 #22
0
ファイル: test_charm.py プロジェクト: phvalguima/operator
 def test_empty_action(self):
     meta = CharmMeta.from_yaml('name: my-charm', '')
     self.assertEqual(meta.actions, {})
コード例 #23
0
    def test_storage_events(self):
        class MyCharm(CharmBase):
            def __init__(self, *args):
                super().__init__(*args)
                self.seen = []
                self.framework.observe(self.on['stor1'].storage_attached, self)
                self.framework.observe(self.on['stor2'].storage_detaching,
                                       self)
                self.framework.observe(self.on['stor3'].storage_attached, self)
                self.framework.observe(self.on['stor-4'].storage_attached,
                                       self)

            def on_stor1_storage_attached(self, event):
                self.seen.append(f'{type(event).__name__}')

            def on_stor2_storage_detaching(self, event):
                self.seen.append(f'{type(event).__name__}')

            def on_stor3_storage_attached(self, event):
                self.seen.append(f'{type(event).__name__}')

            def on_stor_4_storage_attached(self, event):
                self.seen.append(f'{type(event).__name__}')

        self.meta = CharmMeta({
            'name': 'my-charm',
            'storage': {
                'stor1': {
                    'type': 'filesystem'
                },
                'stor2': {
                    'type': 'filesystem',
                    'multiple': {
                        'range': '2',
                    },
                },
                'stor3': {
                    'type': 'filesystem',
                    'multiple': {
                        'range': '2-',
                    },
                },
                'stor-4': {
                    'type': 'filesystem',
                    'multiple': {
                        'range': '2-4',
                    },
                },
            },
        })

        self.assertIsNone(self.meta.storages['stor1'].multiple_range)
        self.assertEqual(self.meta.storages['stor2'].multiple_range, (2, 2))
        self.assertEqual(self.meta.storages['stor3'].multiple_range, (2, None))
        self.assertEqual(self.meta.storages['stor-4'].multiple_range, (2, 4))

        charm = MyCharm(self.create_framework(), None)

        charm.on['stor1'].storage_attached.emit()
        charm.on['stor2'].storage_detaching.emit()
        charm.on['stor3'].storage_attached.emit()
        charm.on['stor-4'].storage_attached.emit()

        self.assertEqual(charm.seen, [
            'StorageAttachedEvent',
            'StorageDetachingEvent',
            'StorageAttachedEvent',
            'StorageAttachedEvent',
        ])
コード例 #24
0
ファイル: test_main.py プロジェクト: zzehring/operator
 def test_not_if_not_in_k8s(self):
     meta = CharmMeta.from_yaml("series: [ecs]")
     with patch.dict(os.environ, {"JUJU_VERSION": "2.8"}):
         self.assertFalse(_should_use_controller_storage(Path("/xyzzy"), meta))
         self.assertLogged('Using local storage: not a kubernetes charm')
コード例 #25
0
ファイル: test_main.py プロジェクト: zzehring/operator
 def test_fallback_to_current_juju_version__too_old(self):
     meta = CharmMeta.from_yaml("series: [kubernetes]")
     with patch.dict(os.environ, {"JUJU_VERSION": "1.0"}):
         self.assertFalse(_should_use_controller_storage(Path("/xyzzy"), meta))
         self.assertLogged('Using local storage: JUJU_VERSION=1.0.0')
コード例 #26
0
    def test_storage_events(self):
        this = self

        class MyCharm(CharmBase):
            def __init__(self, *args):
                super().__init__(*args)
                self.seen = []
                self.framework.observe(self.on['stor1'].storage_attached, self._on_stor1_attach)
                self.framework.observe(self.on['stor2'].storage_detaching, self._on_stor2_detach)
                self.framework.observe(self.on['stor3'].storage_attached, self._on_stor3_attach)
                self.framework.observe(self.on['stor-4'].storage_attached, self._on_stor4_attach)

            def _on_stor1_attach(self, event):
                self.seen.append(type(event).__name__)
                this.assertEqual(event.storage.location, Path("/var/srv/stor1/0"))

            def _on_stor2_detach(self, event):
                self.seen.append(type(event).__name__)

            def _on_stor3_attach(self, event):
                self.seen.append(type(event).__name__)

            def _on_stor4_attach(self, event):
                self.seen.append(type(event).__name__)

        # language=YAML
        self.meta = CharmMeta.from_yaml('''
name: my-charm
storage:
  stor-4:
    multiple:
      range: 2-4
    type: filesystem
  stor1:
    type: filesystem
  stor2:
    multiple:
      range: "2"
    type: filesystem
  stor3:
    multiple:
      range: 2-
    type: filesystem
  stor-multiple-dashes:
    multiple:
      range: 2-
    type: filesystem
''')

        fake_script(
            self,
            "storage-get",
            """
            if [ "$1" = "-s" ]; then
                id=${2#*/}
                key=${2%/*}
                echo "\\"/var/srv/${key}/${id}\\"" # NOQA: test_quote_backslashes
            elif [ "$1" = '--help' ]; then
                printf '%s\\n' \\
                'Usage: storage-get [options] [<key>]' \\
                '   ' \\
                'Summary:' \\
                'print information for storage instance with specified id' \\
                '   ' \\
                'Options:' \\
                '--format  (= smart)' \\
                '    Specify output format (json|smart|yaml)' \\
                '-o, --output (= "")' \\
                '    Specify an output file' \\
                '-s  (= test-stor/0)' \\
                '    specify a storage instance by id' \\
                '   ' \\
                'Details:' \\
                'When no <key> is supplied, all keys values are printed.'
            else
                # Return the same path for all disks since `storage-get`
                # on attach and detach takes no parameters and is not
                # deterministically faked with fake_script
                exit 1
            fi
            """,
        )
        fake_script(
            self,
            "storage-list",
            """
            echo '["disks/0"]'
            """,
        )

        self.assertIsNone(self.meta.storages['stor1'].multiple_range)
        self.assertEqual(self.meta.storages['stor2'].multiple_range, (2, 2))
        self.assertEqual(self.meta.storages['stor3'].multiple_range, (2, None))
        self.assertEqual(self.meta.storages['stor-4'].multiple_range, (2, 4))

        charm = MyCharm(self.create_framework())

        charm.on['stor1'].storage_attached.emit(Storage("stor1", 0, charm.model._backend))
        charm.on['stor2'].storage_detaching.emit(Storage("stor2", 0, charm.model._backend))
        charm.on['stor3'].storage_attached.emit(Storage("stor3", 0, charm.model._backend))
        charm.on['stor-4'].storage_attached.emit(Storage("stor-4", 0, charm.model._backend))
        charm.on['stor-multiple-dashes'].storage_attached.emit(
            Storage("stor-multiple-dashes", 0, charm.model._backend))

        self.assertEqual(charm.seen, [
            'StorageAttachedEvent',
            'StorageDetachingEvent',
            'StorageAttachedEvent',
            'StorageAttachedEvent',
        ])