Example #1
0
    def test_relation_data_del_key(self):
        fake_script(self, 'relation-ids',
                    """[ "$1" = db1 ] && echo '["db1:4"]' || echo '[]'""")
        fake_script(self, 'relation-list',
                    """[ "$2" = 4 ] && echo '["remoteapp1/0"]' || exit 2""")
        fake_script(self, 'relation-set',
                    '''[ "$2" = 4 ] && exit 0 || exit 2''')
        fake_script(
            self, 'relation-get',
            """([ "$2" = 4 ] && [ "$4" = "myapp/0" ]) && echo '{"host": "bar"}' || exit 2"""
        )

        rel_db1 = self.model.get_relation('db1')
        # Force memory cache to be loaded.
        self.assertIn('host', rel_db1.data[self.model.unit])
        del rel_db1.data[self.model.unit]['host']
        fake_script(
            self, 'relation-get',
            """([ "$2" = 4 ] && [ "$4" = "myapp/0" ]) && echo '{}' || exit 2"""
        )
        self.assertNotIn('host', rel_db1.data[self.model.unit])

        self.assertEqual(
            fake_script_calls(self),
            [['relation-ids', 'db1', '--format=json'],
             ['relation-list', '-r', '4', '--format=json'],
             [
                 'relation-get', '-r', '4', '-', 'myapp/0', '--app=False',
                 '--format=json'
             ], ['relation-set', '-r', '4', 'host=', '--app=False']])
Example #2
0
 def test_application_version_set_invalid(self):
     fake_script(self, 'application-version-set', 'exit 0')
     with self.assertRaises(TypeError):
         self.backend.application_version_set(2)
     with self.assertRaises(TypeError):
         self.backend.application_version_set()
     self.assertEqual(fake_script_calls(self), [])
Example #3
0
    def test_remote_app_status(self):
        fake_script(self, 'relation-ids',
                    """[ "$1" = db1 ] && echo '["db1:4"]' || echo '[]'""")
        fake_script(
            self, 'relation-list',
            """[ "$2" = 4 ] && echo '["remoteapp1/0", "remoteapp1/1"]' || exit 2"""
        )

        remoteapp1 = self.model.get_relation('db1').app

        # Remote application status is always unknown.
        self.assertIsInstance(remoteapp1.status, ops.model.UnknownStatus)

        test_statuses = (
            ops.model.UnknownStatus(),
            ops.model.ActiveStatus(),
            ops.model.MaintenanceStatus('Upgrading software'),
            ops.model.BlockedStatus('Awaiting manual resolution'),
            ops.model.WaitingStatus('Awaiting related app updates'),
        )
        for target_status in test_statuses:
            with self.assertRaises(RuntimeError):
                remoteapp1.status = target_status

        self.assertEqual(fake_script_calls(self, clear=True), [
            ['relation-ids', 'db1', '--format=json'],
            ['relation-list', '-r', '4', '--format=json'],
        ])
Example #4
0
    def test_app_relation_data_modify_local_as_leader(self):
        fake_script(self, 'relation-ids',
                    """[ "$1" = db1 ] && echo '["db1:4"]' || echo '[]'""")
        fake_script(
            self, 'relation-list',
            """[ "$2" = 4 ] && echo '["remoteapp1/0", "remoteapp1/1"]' || exit 2"""
        )
        fake_script(
            self, 'relation-get',
            """[ "$2" = 4 ] && [ "$4" = myapp ] && echo '{"password": "******"}' || exit 2"""
        )
        fake_script(self, 'relation-set',
                    """[ "$2" = 4 ] && exit 0 || exit 2""")
        fake_script(self, 'is-leader', 'echo true')

        local_app = self.model.unit.app

        rel_db1 = self.model.get_relation('db1')
        self.assertEqual(rel_db1.data[local_app], {'password': '******'})

        rel_db1.data[local_app]['password'] = '******'

        self.assertEqual(rel_db1.data[local_app]['password'], 'foo')

        self.assertEqual(fake_script_calls(self), [
            ['relation-ids', 'db1', '--format=json'],
            ['relation-list', '-r', '4', '--format=json'],
            [
                'relation-get', '-r', '4', '-', 'myapp', '--app=True',
                '--format=json'
            ],
            ['is-leader', '--format=json'],
            ['relation-set', '-r', '4', 'password=foo', '--app=True'],
        ])
Example #5
0
 def test_action_log_error(self):
     fake_script(self, 'action-get', '')
     fake_script(self, 'action-log', f'echo fooerror >&2 ; exit 1')
     with self.assertRaises(ops.model.ModelError):
         self.backend.action_log('log-message')
     calls = [["action-log", "log-message"]]
     self.assertEqual(fake_script_calls(self, clear=True), calls)
Example #6
0
 def test_action_get_error(self):
     fake_script(self, 'action-get', '')
     fake_script(self, 'action-get', f'echo fooerror >&2 ; exit 1')
     with self.assertRaises(ops.model.ModelError):
         self.backend.action_get()
     calls = [['action-get', '--format=json']]
     self.assertEqual(fake_script_calls(self, clear=True), calls)
Example #7
0
 def test_action_set_error(self):
     fake_script(self, 'action-get', '')
     fake_script(self, 'action-set', f'echo fooerror >&2 ; exit 1')
     with self.assertRaises(ops.model.ModelError):
         self.backend.action_set({'foo': 'bar', 'dead': 'beef cafe'})
     calls = [["action-set", "foo=bar", "dead=beef cafe"]]
     self.assertEqual(fake_script_calls(self, clear=True), calls)
Example #8
0
 def test_storage_tool_errors(self):
     test_cases = [(
         lambda: fake_script(self, 'storage-list', 'echo fooerror >&2 ; exit 1'),
         lambda: self.backend.storage_list('foobar'),
         ops.model.ModelError,
         [['storage-list', 'foobar', '--format=json']],
     ), (
         lambda: fake_script(self, 'storage-get', 'echo fooerror >&2 ; exit 1'),
         lambda: self.backend.storage_get('foobar', 'someattr'),
         ops.model.ModelError,
         [['storage-get', '-s', 'foobar', 'someattr', '--format=json']],
     ), (
         lambda: fake_script(self, 'storage-add', 'echo fooerror >&2 ; exit 1'),
         lambda: self.backend.storage_add('foobar', count=2),
         ops.model.ModelError,
         [['storage-add', 'foobar=2']],
     ), (
         lambda: fake_script(self, 'storage-add', 'echo fooerror >&2 ; exit 1'),
         lambda: self.backend.storage_add('foobar', count=object),
         TypeError,
         [],
     ), (
         lambda: fake_script(self, 'storage-add', 'echo fooerror >&2 ; exit 1'),
         lambda: self.backend.storage_add('foobar', count=True),
         TypeError,
         [],
     )]
     for do_fake, run, exception, calls in test_cases:
         do_fake()
         with self.assertRaises(exception):
             run()
         self.assertEqual(fake_script_calls(self, clear=True), calls)
Example #9
0
    def test_network_get_errors(self):
        err_no_endpoint = 'ERROR no network config found for binding "$2"'
        err_no_rel = 'ERROR invalid value "$3" for option -r: relation not found'

        test_cases = [
            (
                lambda: fake_script(self, 'network-get',
                                    f'echo {err_no_endpoint} >&2 ; exit 1'),
                lambda: self.backend.network_get("deadbeef"),
                ops.model.ModelError,
                [['network-get', 'deadbeef', '--format=json']],
            ),
            (
                lambda: fake_script(self, 'network-get',
                                    f'echo {err_no_rel} >&2 ; exit 2'),
                lambda: self.backend.network_get("deadbeef", 3),
                ops.model.RelationNotFoundError,
                [['network-get', 'deadbeef', '-r', '3', '--format=json']],
            )
        ]
        for do_fake, run, exception, calls in test_cases:
            do_fake()
            with self.assertRaises(exception):
                run()
            self.assertEqual(fake_script_calls(self, clear=True), calls)
Example #10
0
    def test_network_get(self):
        network_get_out = '''{
  "bind-addresses": [
    {
      "mac-address": "",
      "interface-name": "",
      "addresses": [
        {
          "hostname": "",
          "value": "192.0.2.2",
          "cidr": ""
        }
      ]
    }
  ],
  "egress-subnets": [
    "192.0.2.2/32"
  ],
  "ingress-addresses": [
    "192.0.2.2"
  ]
}'''
        fake_script(
            self, 'network-get',
            f'''[ "$1" = deadbeef ] && echo '{network_get_out}' || exit 1''')
        network_info = self.backend.network_get('deadbeef')
        self.assertEqual(network_info, json.loads(network_get_out))
        self.assertEqual(fake_script_calls(self, clear=True),
                         [['network-get', 'deadbeef', '--format=json']])

        network_info = self.backend.network_get('deadbeef', 1)
        self.assertEqual(network_info, json.loads(network_get_out))
        self.assertEqual(
            fake_script_calls(self, clear=True),
            [['network-get', 'deadbeef', '-r', '1', '--format=json']])
Example #11
0
    def test_set_app_status_non_leader_raises(self):
        fake_script(self, 'is-leader', 'echo false')

        with self.assertRaises(RuntimeError):
            self.model.app.status

        with self.assertRaises(RuntimeError):
            self.model.app.status = ops.model.ActiveStatus()
Example #12
0
 def test_action_get(self):
     fake_script(self, 'action-get',
                 """echo '{"foo-name": "bar", "silent": false}'""")
     params = self.backend.action_get()
     self.assertEqual(params['foo-name'], 'bar')
     self.assertEqual(params['silent'], False)
     self.assertEqual(fake_script_calls(self),
                      [['action-get', '--format=json']])
Example #13
0
    def test_binding_by_relation_name(self):
        fake_script(self, 'network-get',
                    '''[ "$1" = db0 ] && echo '{}' || exit 1'''.format(self.network_get_out))
        binding_name = 'db0'
        expected_calls = [['network-get', 'db0', '--format=json']]

        binding = self.model.get_binding(binding_name)
        self._check_binding_data(binding_name, binding)
        self.assertEqual(fake_script_calls(self, clear=True), expected_calls)
Example #14
0
    def test_is_leader(self):
        def check_remote_units():
            fake_script(self, 'relation-ids',
                        """[ "$1" = db1 ] && echo '["db1:4"]' || echo '[]'""")

            fake_script(
                self, 'relation-list',
                """[ "$2" = 4 ] && echo '["remoteapp1/0", "remoteapp1/1"]' || exit 2"""
            )

            # Cannot determine leadership for remote units.
            for u in self.model.get_relation('db1').units:
                with self.assertRaises(RuntimeError):
                    u.is_leader()

        fake_script(self, 'is-leader', 'echo true')
        self.assertTrue(self.model.unit.is_leader())

        check_remote_units()

        # Create a new model and backend to drop a cached is-leader output.
        self.backend = ops.model.ModelBackend()
        meta = ops.charm.CharmMeta()
        meta.relations = {
            'db0':
            RelationMeta('provides', 'db0', {
                'interface': 'db0',
                'scope': 'global'
            }),
            'db1':
            RelationMeta('requires', 'db1', {
                'interface': 'db1',
                'scope': 'global'
            }),
            'db2':
            RelationMeta('peers', 'db2', {
                'interface': 'db2',
                'scope': 'global'
            }),
        }
        self.model = ops.model.Model('myapp/0', meta, self.backend)

        fake_script(self, 'is-leader', 'echo false')
        self.assertFalse(self.model.unit.is_leader())

        check_remote_units()

        self.assertEqual(fake_script_calls(self), [
            ['is-leader', '--format=json'],
            ['relation-ids', 'db1', '--format=json'],
            ['relation-list', '-r', '4', '--format=json'],
            ['is-leader', '--format=json'],
            ['relation-ids', 'db1', '--format=json'],
            ['relation-list', '-r', '4', '--format=json'],
        ])
Example #15
0
 def test_get(self):
     fake_script(self, 'state-get',
                 dedent("""
         echo 'foo: "bar"'
         """))
     backend = storage._JujuStorageBackend()
     value = backend.get('key')
     self.assertEqual(value, {'foo': 'bar'})
     self.assertEqual(fake_script_calls(self, clear=True), [
         ['state-get', 'key'],
     ])
Example #16
0
        def check_remote_units():
            fake_script(self, 'relation-ids',
                        """[ "$1" = db1 ] && echo '["db1:4"]' || echo '[]'""")

            fake_script(
                self, 'relation-list',
                """[ "$2" = 4 ] && echo '["remoteapp1/0", "remoteapp1/1"]' || exit 2"""
            )

            # Cannot determine leadership for remote units.
            for u in self.model.get_relation('db1').units:
                with self.assertRaises(RuntimeError):
                    u.is_leader()
Example #17
0
 def test_binding_by_relation(self):
     fake_script(self, 'network-get',
                 '''[ "$1" = db0 ] && echo '{}' || exit 1'''.format(self.network_get_out))
     binding_name = 'db0'
     expected_calls = [
         ['relation-ids', 'db0', '--format=json'],
         # The two invocations below are due to the get_relation call.
         ['relation-list', '-r', '4', '--format=json'],
         ['network-get', 'db0', '-r', '4', '--format=json'],
     ]
     binding = self.model.get_binding(self.model.get_relation(binding_name))
     self._check_binding_data(binding_name, binding)
     self.assertEqual(fake_script_calls(self, clear=True), expected_calls)
Example #18
0
    def test_config(self):
        fake_script(self, 'config-get',
                    """echo '{"foo":"foo","bar":1,"qux":true}'""")
        self.assertEqual(self.model.config, {
            'foo': 'foo',
            'bar': 1,
            'qux': True,
        })
        with self.assertRaises(TypeError):
            # Confirm that we cannot modify config values.
            self.model.config['foo'] = 'bar'

        self.assertEqual(fake_script_calls(self),
                         [['config-get', '--format=json']])
Example #19
0
    def test_relation_set_fail(self):
        fake_script(self, 'relation-ids',
                    """[ "$1" = db2 ] && echo '["db2:5"]' || echo '[]'""")
        fake_script(self, 'relation-list',
                    """[ "$2" = 5 ] && echo '["remoteapp1/0"]' || exit 2""")
        fake_script(
            self, 'relation-get',
            """([ "$2" = 5 ] && [ "$4" = "myapp/0" ]) && echo '{"host": "myapp-0"}' || exit 2"""
        )
        fake_script(self, 'relation-set', 'exit 2')

        rel_db2 = self.model.relations['db2'][0]
        # Force memory cache to be loaded.
        self.assertIn('host', rel_db2.data[self.model.unit])
        with self.assertRaises(ops.model.ModelError):
            rel_db2.data[self.model.unit]['host'] = 'bar'
        self.assertEqual(rel_db2.data[self.model.unit]['host'], 'myapp-0')
        with self.assertRaises(ops.model.ModelError):
            del rel_db2.data[self.model.unit]['host']
        self.assertIn('host', rel_db2.data[self.model.unit])

        self.assertEqual(
            fake_script_calls(self),
            [['relation-ids', 'db2', '--format=json'],
             ['relation-list', '-r', '5', '--format=json'],
             [
                 'relation-get', '-r', '5', '-', 'myapp/0', '--app=False',
                 '--format=json'
             ], ['relation-set', '-r', '5', 'host=bar', '--app=False'],
             ['relation-set', '-r', '5', 'host=', '--app=False']])
Example #20
0
    def test_status_is_app_forced_kwargs(self):
        fake_script(self, 'status-get', 'exit 1')
        fake_script(self, 'status-set', 'exit 1')

        test_cases = (
            lambda: self.backend.status_get(False),
            lambda: self.backend.status_get(True),
            lambda: self.backend.status_set('active', '', False),
            lambda: self.backend.status_set('active', '', True),
        )

        for case in test_cases:
            with self.assertRaises(TypeError):
                case()
Example #21
0
 def test_valid_metrics(self):
     fake_script(self, 'add-metric', 'exit 0')
     test_cases = [(
         OrderedDict([('foo', 42), ('b-ar', 4.5), ('ba_-z', 4.5), ('a', 1)]),
         OrderedDict([('de', 'ad'), ('be', 'ef_ -')]),
         [['add-metric', '--labels', 'de=ad,be=ef_ -',
           'foo=42', 'b-ar=4.5', 'ba_-z=4.5', 'a=1']]
     ), (
         OrderedDict([('foo1', 0), ('b2r', 4.5)]),
         OrderedDict([('d3', 'aд'), ('b33f', '3_ -')]),
         [['add-metric', '--labels', 'd3=aд,b33f=3_ -', 'foo1=0', 'b2r=4.5']],
     )]
     for metrics, labels, expected_calls in test_cases:
         self.backend.add_metrics(metrics, labels)
         self.assertEqual(fake_script_calls(self, clear=True), expected_calls)
Example #22
0
    def test_juju_log(self):
        fake_script(self, 'juju-log', 'exit 0')
        self.backend.juju_log('WARNING', 'foo')
        self.assertEqual(fake_script_calls(self, clear=True),
                         [['juju-log', '--log-level', 'WARNING', 'foo']])

        with self.assertRaises(TypeError):
            self.backend.juju_log('DEBUG')
        self.assertEqual(fake_script_calls(self, clear=True), [])

        fake_script(self, 'juju-log', 'exit 1')
        with self.assertRaises(ops.model.ModelError):
            self.backend.juju_log('BAR', 'foo')
        self.assertEqual(fake_script_calls(self, clear=True),
                         [['juju-log', '--log-level', 'BAR', 'foo']])
Example #23
0
    def test_remote_units_is_our(self):
        fake_script(self, 'relation-ids',
                    """[ "$1" = db1 ] && echo '["db1:4"]' || echo '[]'""")
        fake_script(
            self, 'relation-list',
            """[ "$2" = 4 ] && echo '["remoteapp1/0", "remoteapp1/1"]' || exit 2"""
        )

        for u in self.model.get_relation('db1').units:
            self.assertFalse(u._is_our_unit)
            self.assertFalse(u.app._is_our_app)

        self.assertEqual(fake_script_calls(self),
                         [['relation-ids', 'db1', '--format=json'],
                          ['relation-list', '-r', '4', '--format=json']])
Example #24
0
    def test_resources(self):
        meta = ops.charm.CharmMeta()
        meta.resources = {'foo': None, 'bar': None}
        model = ops.model.Model('myapp/0', meta, self.backend)

        with self.assertRaises(RuntimeError):
            model.resources.fetch('qux')

        fake_script(self, 'resource-get', 'exit 1')
        with self.assertRaises(ops.model.ModelError):
            model.resources.fetch('foo')

        fake_script(
            self, 'resource-get',
            'echo /var/lib/juju/agents/unit-test-0/resources/$1/$1.tgz')
        self.assertEqual(model.resources.fetch('foo').name, 'foo.tgz')
        self.assertEqual(model.resources.fetch('bar').name, 'bar.tgz')
Example #25
0
    def test_resources(self):
        # TODO: (jam) 2020-05-07 Harness doesn't yet support resource-get issue #262
        meta = ops.charm.CharmMeta()
        meta.resources = {'foo': None, 'bar': None}
        model = ops.model.Model('myapp/0', meta, ops.model._ModelBackend())

        with self.assertRaises(RuntimeError):
            model.resources.fetch('qux')

        fake_script(self, 'resource-get', 'exit 1')
        with self.assertRaises(ops.model.ModelError):
            model.resources.fetch('foo')

        fake_script(self, 'resource-get',
                    'echo /var/lib/juju/agents/unit-test-0/resources/$1/$1.tgz')
        self.assertEqual(model.resources.fetch('foo').name, 'foo.tgz')
        self.assertEqual(model.resources.fetch('bar').name, 'bar.tgz')
Example #26
0
 def test_dead_relations(self):
     fake_script(
         self,
         'network-get',
         '''
             if [ "$1" = db0 ] && [ "$2" = --format=json ]; then
                 echo '{}'
             else
                 echo ERROR invalid value "$2" for option -r: relation not found >&2
                 exit 2
             fi
         '''.format(self.network_get_out))
     # Validate the behavior for dead relations.
     binding = ops.model.Binding('db0', 42, self.model._backend)
     self.assertEqual(binding.network.bind_address, ipaddress.ip_address('192.0.2.2'))
     self.assertEqual(fake_script_calls(self, clear=True), [
         ['network-get', 'db0', '-r', '42', '--format=json'],
         ['network-get', 'db0', '--format=json'],
     ])
Example #27
0
    def test_local_set_invalid_status(self):
        fake_script(self, 'status-set', 'exit 1')
        fake_script(self, 'is-leader', 'echo true')

        with self.assertRaises(ops.model.ModelError):
            self.model.unit.status = ops.model.UnknownStatus()

        self.assertEqual(fake_script_calls(self, True), [
            ['status-set', '--application=False', 'unknown', ''],
        ])

        with self.assertRaises(ops.model.ModelError):
            self.model.app.status = ops.model.UnknownStatus()

        # A leadership check is needed for application status.
        self.assertEqual(fake_script_calls(self, True), [
            ['is-leader', '--format=json'],
            ['status-set', '--application=True', 'unknown', ''],
        ])
Example #28
0
    def test_relations_keys(self):
        fake_script(
            self, 'relation-ids',
            """[ "$1" = db2 ] && echo '["db2:5", "db2:6"]' || echo '[]'""")
        fake_script(
            self, 'relation-list',
            """([ "$2" = 5 ] && echo '["remoteapp1/0", "remoteapp1/1"]') || ([ "$2" = 6 ] && echo '["remoteapp2/0"]') || exit 2"""
        )

        for relation in self.model.relations['db2']:
            self.assertIn(self.model.unit, relation.data)
            unit_from_rel = next(
                filter(lambda u: u.name == 'myapp/0', relation.data.keys()))
            self.assertIs(self.model.unit, unit_from_rel)

        self.assertEqual(fake_script_calls(self),
                         [['relation-ids', 'db2', '--format=json'],
                          ['relation-list', '-r', '5', '--format=json'],
                          ['relation-list', '-r', '6', '--format=json']])
Example #29
0
 def test_set_encodes_args(self):
     t = tempfile.NamedTemporaryFile()
     fake_script(
         self, 'state-set',
         dedent("""
         cat >> {}
         """).format(pathlib.Path(t.name).as_posix()))
     backend = storage._JujuStorageBackend()
     backend.set('key', {'foo': 2})
     self.assertEqual(fake_script_calls(self, clear=True), [
         ['state-set', '--file', '-'],
     ])
     t.seek(0)
     content = t.read()
     self.assertEqual(
         content.decode('utf-8'),
         dedent("""\
         "key": |
           {foo: 2}
         """))
Example #30
0
    def test_local_set_valid_unit_status(self):
        test_cases = [
            (
                ops.model.ActiveStatus(),
                lambda: fake_script(self, 'status-set', 'exit 0'),
                lambda: self.assertEqual(fake_script_calls(self, True), [[
                    'status-set', '--application=False', 'active', ''
                ]]),
            ),
            (
                ops.model.MaintenanceStatus('Yellow'),
                lambda: fake_script(self, 'status-set', 'exit 0'),
                lambda: self.assertEqual(fake_script_calls(self, True), [[
                    'status-set', '--application=False', 'maintenance',
                    'Yellow'
                ]]),
            ),
            (
                ops.model.BlockedStatus('Red'),
                lambda: fake_script(self, 'status-set', 'exit 0'),
                lambda: self.assertEqual(fake_script_calls(self, True), [[
                    'status-set', '--application=False', 'blocked', 'Red'
                ]]),
            ),
            (
                ops.model.WaitingStatus('White'),
                lambda: fake_script(self, 'status-set', 'exit 0'),
                lambda: self.assertEqual(fake_script_calls(self, True), [[
                    'status-set', '--application=False', 'waiting', 'White'
                ]]),
            )
        ]

        for target_status, setup_tools, check_tool_calls in test_cases:
            setup_tools()

            self.model.unit.status = target_status

            self.assertEqual(self.model.unit.status, target_status)

            check_tool_calls()