Exemplo n.º 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
Exemplo n.º 2
0
    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',
        ])
Exemplo n.º 3
0
 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))
Exemplo n.º 4
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
''')
Exemplo n.º 5
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
''')
Exemplo n.º 6
0
    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')
Exemplo n.º 7
0
    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."
''')
Exemplo n.º 8
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
Exemplo n.º 9
0
    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)
Exemplo n.º 10
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')
Exemplo n.º 11
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
Exemplo n.º 12
0
 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')
Exemplo n.º 13
0
 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')
Exemplo n.º 14
0
 def test_empty_action(self):
     meta = CharmMeta.from_yaml('name: my-charm', '')
     self.assertEqual(meta.actions, {})
Exemplo n.º 15
0
 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')
Exemplo n.º 16
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._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',
        ])
Exemplo n.º 17
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',
        ])