1111-include (" vhost.hrl" ).
1212
1313-export ([recover /0 , recover /1 , read_config /1 ]).
14- -export ([add /2 , add /3 , add /4 , delete /2 , exists /1 , assert /1 ,
14+ -export ([add /2 , add /3 , add /4 , delete /2 , delete_ignoring_protection / 2 , exists /1 , assert /1 ,
1515 set_limits /2 , vhost_cluster_state /1 , is_running_on_all_nodes /1 , await_running_on_all_nodes /2 ,
1616 list /0 , count /0 , list_names /0 , all /0 , all_tagged_with /1 ]).
1717-export ([parse_tags /1 , update_tags /3 ]).
18- -export ([update_metadata /3 ]).
18+ -export ([update_metadata /3 , enable_protection_from_deletion / 1 , disable_protection_from_deletion / 1 ]).
1919-export ([lookup /1 , default_name /0 ]).
2020-export ([info /1 , info /2 , info_all /0 , info_all /1 , info_all /2 , info_all /3 ]).
2121-export ([dir /1 , msg_store_dir_path /1 , msg_store_dir_wildcard /0 , msg_store_dir_base /0 , config_file_path /1 , ensure_config_file /1 ]).
@@ -253,20 +253,37 @@ declare_default_exchanges(VHostName, ActingUser) ->
253253 end , DefaultExchanges ).
254254
255255-spec update_metadata (vhost :name (), vhost :metadata (), rabbit_types :username ()) -> rabbit_types :ok_or_error (any ()).
256+ update_metadata (Name , undefined , _ActingUser ) ->
257+ case rabbit_db_vhost :exists (Name ) of
258+ true ->
259+ ok ;
260+ false ->
261+ {error , {no_such_vhost , Name }}
262+ end ;
263+ update_metadata (Name , Metadata0 , _ActingUser ) when is_map (Metadata0 ) andalso map_size (Metadata0 ) =:= 0 ->
264+ case rabbit_db_vhost :exists (Name ) of
265+ true ->
266+ ok ;
267+ false ->
268+ {error , {no_such_vhost , Name }}
269+ end ;
256270update_metadata (Name , Metadata0 , ActingUser ) ->
257- Metadata = maps :with ([description , tags , default_queue_type ], Metadata0 ),
271+ KnownKeys = [description , tags , default_queue_type , protected_from_deletion ],
272+ Metadata = maps :with (KnownKeys , Metadata0 ),
258273
259274 case rabbit_db_vhost :merge_metadata (Name , Metadata ) of
260275 {ok , VHost } ->
261276 Description = vhost :get_description (VHost ),
262277 Tags = vhost :get_tags (VHost ),
263278 DefaultQueueType = vhost :get_default_queue_type (VHost ),
279+ IsProtected = vhost :is_protected_from_deletion (VHost ),
264280 rabbit_event :notify (
265281 vhost_updated ,
266282 info (VHost ) ++ [{user_who_performed_action , ActingUser },
267283 {description , Description },
268284 {tags , Tags },
269- {default_queue_type , DefaultQueueType }]),
285+ {default_queue_type , DefaultQueueType },
286+ {deletion_protection , IsProtected }]),
270287 ok ;
271288 {error , _ } = Error ->
272289 Error
@@ -278,45 +295,61 @@ update(Name, Description, Tags, DefaultQueueType, ActingUser) ->
278295 update_metadata (Name , Metadata , ActingUser ).
279296
280297-spec delete (vhost :name (), rabbit_types :username ()) -> rabbit_types :ok_or_error (any ()).
281- delete (VHost , ActingUser ) ->
298+ delete (Name , ActingUser ) ->
299+ case rabbit_db_vhost :get (Name ) of
300+ % % preserve the original behavior for backwards compatibility
301+ undefined -> delete_ignoring_protection (Name , ActingUser );
302+ VHost ->
303+ case vhost :is_protected_from_deletion (VHost ) of
304+ true ->
305+ Msg = " Refusing to delete virtual host '~ts ' because it is protected from deletion" ,
306+ rabbit_log :debug (Msg , [Name ]),
307+ {error , protected_from_deletion };
308+ false ->
309+ delete_ignoring_protection (Name , ActingUser )
310+ end
311+ end .
312+
313+ -spec delete_ignoring_protection (vhost :name (), rabbit_types :username ()) -> rabbit_types :ok_or_error (any ()).
314+ delete_ignoring_protection (Name , ActingUser ) ->
282315 % % FIXME: We are forced to delete the queues and exchanges outside
283316 % % the TX below. Queue deletion involves sending messages to the queue
284317 % % process, which in turn results in further database actions and
285318 % % eventually the termination of that process. Exchange deletion causes
286319 % % notifications which must be sent outside the TX
287- rabbit_log :info (" Deleting vhost '~ts '" , [VHost ]),
320+ rabbit_log :info (" Deleting vhost '~ts '" , [Name ]),
288321 % % TODO: This code does a lot of "list resources, walk through the list to
289322 % % delete each resource". This feature should be provided by each called
290323 % % modules, like `rabbit_amqqueue:delete_all_for_vhost(VHost)'. These new
291324 % % calls would be responsible for the atomicity, not this code.
292325 % % Clear the permissions first to prohibit new incoming connections when deleting a vhost
293- rabbit_log :info (" Clearing permissions in vhost '~ts ' because it's being deleted" , [VHost ]),
294- ok = rabbit_auth_backend_internal :clear_all_permissions_for_vhost (VHost , ActingUser ),
295- rabbit_log :info (" Deleting queues in vhost '~ts ' because it's being deleted" , [VHost ]),
326+ rabbit_log :info (" Clearing permissions in vhost '~ts ' because it's being deleted" , [Name ]),
327+ ok = rabbit_auth_backend_internal :clear_all_permissions_for_vhost (Name , ActingUser ),
328+ rabbit_log :info (" Deleting queues in vhost '~ts ' because it's being deleted" , [Name ]),
296329 QDelFun = fun (Q ) -> rabbit_amqqueue :delete (Q , false , false , ActingUser ) end ,
297330 [begin
298- Name = amqqueue :get_name (Q ),
299- assert_benign (rabbit_amqqueue :with (Name , QDelFun ), ActingUser )
300- end || Q <- rabbit_amqqueue :list (VHost )],
301- rabbit_log :info (" Deleting exchanges in vhost '~ts ' because it's being deleted" , [VHost ]),
302- ok = rabbit_exchange :delete_all (VHost , ActingUser ),
303- rabbit_log :info (" Clearing policies and runtime parameters in vhost '~ts ' because it's being deleted" , [VHost ]),
304- _ = rabbit_runtime_parameters :clear_vhost (VHost , ActingUser ),
305- rabbit_log :debug (" Removing vhost '~ts ' from the metadata storage because it's being deleted" , [VHost ]),
306- Ret = case rabbit_db_vhost :delete (VHost ) of
331+ QName = amqqueue :get_name (Q ),
332+ assert_benign (rabbit_amqqueue :with (QName , QDelFun ), ActingUser )
333+ end || Q <- rabbit_amqqueue :list (Name )],
334+ rabbit_log :info (" Deleting exchanges in vhost '~ts ' because it's being deleted" , [Name ]),
335+ ok = rabbit_exchange :delete_all (Name , ActingUser ),
336+ rabbit_log :info (" Clearing policies and runtime parameters in vhost '~ts ' because it's being deleted" , [Name ]),
337+ _ = rabbit_runtime_parameters :clear_vhost (Name , ActingUser ),
338+ rabbit_log :debug (" Removing vhost '~ts ' from the metadata storage because it's being deleted" , [Name ]),
339+ Ret = case rabbit_db_vhost :delete (Name ) of
307340 true ->
308341 ok = rabbit_event :notify (
309342 vhost_deleted ,
310- [{name , VHost },
343+ [{name , Name },
311344 {user_who_performed_action , ActingUser }]);
312345 false ->
313- {error , {no_such_vhost , VHost }};
346+ {error , {no_such_vhost , Name }};
314347 {error , _ } = Err ->
315348 Err
316349 end ,
317350 % % After vhost was deleted from the database, we try to stop vhost
318351 % % supervisors on all the nodes.
319- rabbit_vhost_sup_sup :delete_on_all_nodes (VHost ),
352+ rabbit_vhost_sup_sup :delete_on_all_nodes (Name ),
320353 Ret .
321354
322355-spec put_vhost (vhost :name (),
@@ -530,6 +563,14 @@ lookup(VHostName) ->
530563 VHost -> VHost
531564 end .
532565
566+ -spec enable_protection_from_deletion (vhost :name ()) -> vhost :vhost () | rabbit_types :ok_or_error (any ()).
567+ enable_protection_from_deletion (VHostName ) ->
568+ rabbit_db_vhost :enable_protection_from_deletion (VHostName ).
569+
570+ -spec disable_protection_from_deletion (vhost :name ()) -> vhost :vhost () | rabbit_types :ok_or_error (any ()).
571+ disable_protection_from_deletion (VHostName ) ->
572+ rabbit_db_vhost :disable_protection_from_deletion (VHostName ).
573+
533574-spec assert (vhost :name ()) -> 'ok' .
534575assert (VHostName ) ->
535576 case exists (VHostName ) of
@@ -624,6 +665,7 @@ i(cluster_state, VHost) -> vhost_cluster_state(vhost:get_name(VHost));
624665i (description , VHost ) -> vhost :get_description (VHost );
625666i (tags , VHost ) -> vhost :get_tags (VHost );
626667i (default_queue_type , VHost ) -> rabbit_queue_type :short_alias_of (default_queue_type (vhost :get_name (VHost )));
668+ i (protected_from_deletion , VHost ) -> vhost :is_protected_from_deletion (VHost );
627669i (metadata , VHost ) ->
628670 DQT = rabbit_queue_type :short_alias_of (default_queue_type (vhost :get_name (VHost ))),
629671 case vhost :get_metadata (VHost ) of
0 commit comments