package io.cattle.platform.servicediscovery.api.util.selector; import io.cattle.platform.servicediscovery.api.util.selector.SelectorConstraint.Op; import io.github.ibuildthecloud.gdapi.validation.ValidationErrorCodes; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.StringUtils; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; public class SelectorUtils { private static Cache<String, List<SelectorConstraint<?>>> cache = CacheBuilder.newBuilder() .expireAfterWrite(3600, TimeUnit.SECONDS).build(); public static boolean isSelectorMatch(String selector, Map<String, String> labels) { if (StringUtils.isEmpty(selector)) { return false; } List<SelectorConstraint<?>> constraints = getSelectorConstraints(selector); if (constraints.isEmpty()) { return false; } int found = 0; for (SelectorConstraint<?> constraint : constraints) { if (constraint.isMatch(labels)) { found++; } } if (found != constraints.size()) { return false; } return true; } public static List<SelectorConstraint<?>> getSelectorConstraints(String selector) { List<SelectorConstraint<?>> cachedConstraints = cache.getIfPresent(selector); if (cachedConstraints != null) { return cachedConstraints; } List<SelectorConstraint<?>> constraints = new ArrayList<>(); List<String> selectorConstraints = new ArrayList<>(); // as selector format is: // key in (value1, value2) // key notin (value1, value2) // key // key = value // key != value // and any combination of them can be specified in coma separated way: // key != value, key in (value1, value2), key = value boolean inList = false; StringBuffer selectorConstraint = new StringBuffer(); for (int i = 0; i < selector.length(); i++) { boolean finishConstraint = (i == selector.length() - 1); char currentC = selector.charAt(i); if (currentC == '(') { inList = true; } else if (currentC == ')') { inList = false; } else if (currentC == ',') { if (inList) { selectorConstraint.append(currentC); } else { finishConstraint = true; } } else { selectorConstraint.append(currentC); } if (finishConstraint) { selectorConstraints.add(selectorConstraint.toString()); selectorConstraint = new StringBuffer(); } } for (String constraint : selectorConstraints) { SelectorConstraint<?> newSelectorConstraint = getSelectorConstraint(constraint); if (newSelectorConstraint.key.contains(" ")) { ValidationErrorCodes.throwValidationError(ValidationErrorCodes.INVALID_FORMAT, "Invalid format selector : " + selector); } constraints.add(newSelectorConstraint); } cache.put(selector, constraints); return constraints; } private static SelectorConstraint<?> getSelectorConstraint(String selector) { SelectorConstraint.Op finalOp = Op.NOOP; String key = StringUtils.EMPTY; String value = StringUtils.EMPTY; for (SelectorConstraint.Op op : SelectorConstraint.Op.values()) { if (op == Op.NOOP) { continue; } String[] exp = selector.split(op.getSelectorSymbol(), 2); if (exp.length == 2) { finalOp = op; key = exp[0].trim(); value = exp[1].trim(); break; } } if (finalOp == SelectorConstraint.Op.EQ) { return new SelectorConstraintEq(key, value); } else if (finalOp == SelectorConstraint.Op.NEQ) { return new SelectorConstraintNeq(key, value); } else if (finalOp == SelectorConstraint.Op.IN) { return new SelectorConstraintIn(key, splitValues(value)); } else if (finalOp == SelectorConstraint.Op.NOTIN) { return new SelectorConstraintNotIn(key, splitValues(value)); } else { return new SelectorConstraintNoop(selector.trim(), null); } } protected static List<String> splitValues(String value) { List<String> values = new ArrayList<>(); for (String valueStr : value.split(",")) { values.add(valueStr.trim().toLowerCase()); } return values; } }