| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.cassandra.db.guardrails; |
| |
| import java.util.Set; |
| import java.util.function.Consumer; |
| import java.util.function.Function; |
| import java.util.stream.Collectors; |
| import javax.annotation.Nullable; |
| |
| import com.google.common.collect.Sets; |
| |
| import org.apache.cassandra.service.ClientState; |
| |
| import static java.lang.String.format; |
| |
| /** |
| * A guardrail that warns about some specific values, warns about but ignores some other values, and/or rejects the use |
| * of some other values. |
| * |
| * @param <T> The type of the values of which certain are warned, ignored and/or disallowed. |
| */ |
| public class Values<T> extends Guardrail |
| { |
| private final Function<ClientState, Set<T>> warnedValues; |
| private final Function<ClientState, Set<T>> ignoredValues; |
| private final Function<ClientState, Set<T>> disallowedValues; |
| private final String what; |
| |
| /** |
| * Creates a new values guardrail. |
| * |
| * @param name the identifying name of the guardrail |
| * @param warnedValues a {@link ClientState}-based provider of the values for which a warning is triggered. |
| * @param ignoredValues a {@link ClientState}-based provider of the values that are ignored. |
| * @param disallowedValues a {@link ClientState}-based provider of the values that are disallowed. |
| * @param what The feature that is guarded by this guardrail (for reporting in error messages). |
| */ |
| public Values(String name, |
| Function<ClientState, Set<T>> warnedValues, |
| Function<ClientState, Set<T>> ignoredValues, |
| Function<ClientState, Set<T>> disallowedValues, |
| String what) |
| { |
| super(name); |
| this.warnedValues = warnedValues; |
| this.ignoredValues = ignoredValues; |
| this.disallowedValues = disallowedValues; |
| this.what = what; |
| } |
| |
| /** |
| * Triggers a warning for each of the provided values that is discouraged by this guardrail. If any of the values |
| * is disallowed it will abort the operation. |
| * <p> |
| * This assumes that there aren't any values to be ignored, thus it doesn't require an ignore action. If this is |
| * not the case and the provided value is set up to be ignored this will throw an assertion error. |
| * |
| * @param values The values to check. |
| * @param state The client state, used to skip the check if the query is internal or is done by a superuser. |
| * A {@code null} value means that the check should be done regardless of the query. |
| */ |
| public void guard(Set<T> values, @Nullable ClientState state) |
| { |
| guard(values, x -> { |
| throw new AssertionError(format("There isn't an ignore action for %s, but value %s is setup to be ignored", |
| what, x)); |
| }, state); |
| } |
| |
| /** |
| * Triggers a warning for each of the provided values that is discouraged by this guardrail. Also triggers a warning |
| * for each of the provided values that is ignored by this guardrail and triggers the provided action to ignore it. |
| * If any of the values is disallowed it will abort the operation. |
| * |
| * @param values The values to check. |
| * @param ignoreAction An action called on the subset of {@code values} that should be ignored. This action |
| * should do whatever is necessary to make sure the value is ignored. |
| * @param state The client state, used to skip the check if the query is internal or is done by a superuser. |
| * A {@code null} value means that the check should be done regardless of the query, although it |
| * won't throw any exception if the failure threshold is exceeded. This is so because checks |
| * without an associated client come from asynchronous processes such as compaction, and we |
| * don't want to interrupt such processes. |
| */ |
| public void guard(Set<T> values, Consumer<T> ignoreAction, @Nullable ClientState state) |
| { |
| if (!enabled(state)) |
| return; |
| |
| Set<T> disallowed = disallowedValues.apply(state); |
| Set<T> toDisallow = Sets.intersection(values, disallowed); |
| if (!toDisallow.isEmpty()) |
| fail(format("Provided values %s are not allowed for %s (disallowed values are: %s)", |
| toDisallow.stream().sorted().collect(Collectors.toList()), what, disallowed), state); |
| |
| Set<T> ignored = ignoredValues.apply(state); |
| Set<T> toIgnore = Sets.intersection(values, ignored); |
| if (!toIgnore.isEmpty()) |
| { |
| warn(format("Ignoring provided values %s as they are not supported for %s (ignored values are: %s)", |
| toIgnore.stream().sorted().collect(Collectors.toList()), what, ignored)); |
| toIgnore.forEach(ignoreAction); |
| } |
| |
| Set<T> warned = warnedValues.apply(state); |
| Set<T> toWarn = Sets.intersection(values, warned); |
| if (!toWarn.isEmpty()) |
| warn(format("Provided values %s are not recommended for %s (warned values are: %s)", |
| toWarn.stream().sorted().collect(Collectors.toList()), what, warned)); |
| } |
| } |