def add_layer(self, label: str, layer: typing.Union[str, dict, pebble.Layer], *, combine: bool = False): # I wish we could combine some of this helpful object corralling with the actual backend, # rather than having to re-implement it. Maybe we could subclass if not isinstance(label, str): raise TypeError('label must be a str, not {}'.format( type(label).__name__)) if isinstance(layer, (str, dict)): layer_obj = pebble.Layer(layer) elif isinstance(layer, pebble.Layer): layer_obj = layer else: raise TypeError( 'layer must be str, dict, or pebble.Layer, not {}'.format( type(layer).__name__)) if label in self._layers: # TODO: jam 2021-04-19 These should not be RuntimeErrors but should be proper error # types. https://github.com/canonical/operator/issues/514 if not combine: raise RuntimeError( '400 Bad Request: layer "{}" already exists'.format(label)) layer = self._layers[label] for name, service in layer_obj.services.items(): # 'override' is actually single quoted in the real error, but # it shouldn't be, hopefully that gets cleaned up. if not service.override: raise RuntimeError( '500 Internal Server Error: layer "{}" must define' '"override" for service "{}"'.format(label, name)) if service.override not in ('merge', 'replace'): raise RuntimeError( '500 Internal Server Error: layer "{}" has invalid ' '"override" value on service "{}"'.format(label, name)) if service.override != 'replace': raise RuntimeError( 'override: "{}" unsupported for layer "{}" service "{}"' .format(service.override, label, name)) layer.services[name] = service else: self._layers[label] = layer_obj
def test_add_layer(self): okay_response = { "result": True, "status": "OK", "status-code": 200, "type": "sync" } self.client.responses.append(okay_response) self.client.responses.append(okay_response) self.client.responses.append(okay_response) self.client.responses.append(okay_response) layer_yaml = """ services: foo: command: echo bar override: replace """[1:] layer = pebble.Layer(layer_yaml) self.client.add_layer('a', layer) self.client.add_layer('b', layer.to_yaml()) self.client.add_layer('c', layer.to_dict()) self.client.add_layer('d', layer, combine=True) def build_expected(label, combine): return { 'action': 'add', 'combine': combine, 'label': label, 'format': 'yaml', 'layer': layer_yaml, } self.assertEqual(self.client.requests, [ ('POST', '/v1/layers', None, build_expected('a', False)), ('POST', '/v1/layers', None, build_expected('b', False)), ('POST', '/v1/layers', None, build_expected('c', False)), ('POST', '/v1/layers', None, build_expected('d', True)), ])
def test_no_args(self): s = pebble.Layer() self._assert_empty(s)