Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for label selectors in the mock server #1158

Merged
merged 3 commits into from
Jul 31, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

* Fix #1156 : Watcher does not have correct authentication information in Openshift environment.

* Fix #1125 : ConfigMap labels are ignored when using mock KubernetesServer

* Fix #1144 : Get Request with OpenShift Mock Server Not Working

* Fix #1147: Cluster context was being ignored when loading the Config from a kubeconfig file
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import io.fabric8.mockwebserver.crud.AttributeExtractor;
import io.fabric8.mockwebserver.crud.AttributeSet;
import io.fabric8.zjsonpatch.internal.guava.Strings;
import java.util.Map;
import okhttp3.HttpUrl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -34,9 +36,11 @@ public class KubernetesAttributesExtractor implements AttributeExtractor<HasMeta

private static final Logger LOGGER = LoggerFactory.getLogger(KubernetesAttributesExtractor.class);

public static final String KEY = "key";
public static final String KIND = "kind";
public static final String NAME = "name";
public static final String NAMESPACE = "namespace";
public static final String VALUE = "value";

private static final String API_GROUP = "/o?api(s/[a-zA-Z0-9-_.]+)?";
private static final String VERSION_GROUP = "(/(?<version>[a-zA-Z0-9-_]+))?";
Expand All @@ -47,16 +51,37 @@ public class KubernetesAttributesExtractor implements AttributeExtractor<HasMeta

protected static final Pattern PATTERN = Pattern.compile(API_GROUP + VERSION_GROUP + NAMESPACE_GROUP + KIND_GROUP + NAME_GROUP + END_GROUP);

private static final String LABEL_KEY_PREFIX = "labels:";
private static final String KEY_GROUP = "(?<key>[a-zA-Z0-9-_./]+)";
// Matches a==b and a=b but not a!=b.
private static final String EQUALITY_GROUP = "(==|(?<!!)=)";
private static final String VALUE_GROUP = "(?<value>[a-zA-Z0-9-_.]+)";
private static final Pattern LABEL_REQUIREMENT_EQUALITY = Pattern.compile(KEY_GROUP + EQUALITY_GROUP + VALUE_GROUP);

// These elements are added to the path/query fragment so that it can be parsed by HttpUrl. Their
// values are not important, HttpUrl expects the scheme to be http or https.
private static final String SCHEME = "http";
private static final String HOST = "localhost";

private HttpUrl parseUrlFromPathAndQuery(String s) {
if (!s.startsWith("/")) {
s = "/" + s;
}
return HttpUrl.parse(String.format("%s://%s%s", SCHEME, HOST, s));
}

@Override
public AttributeSet fromPath(String s) {
if (s == null || s.isEmpty()) {
return new AttributeSet();
}

//Get paths
Matcher m = PATTERN.matcher(s);
HttpUrl url = parseUrlFromPathAndQuery(s);
Matcher m = PATTERN.matcher(url.encodedPath());
if (m.matches()) {
AttributeSet set = extract(m);
set = AttributeSet.merge(set, extractQueryParameters(url));
LOGGER.debug("fromPath {} : {}", s, set);
return set;
}
Expand Down Expand Up @@ -89,15 +114,7 @@ public AttributeSet extract(String s) {
return extract(h);
}

//Get paths
Matcher m = PATTERN.matcher(s);
if (m.matches()) {
AttributeSet set = extract(m);
LOGGER.debug("extract {} : {}", s, set);
return set;
}
LOGGER.debug("extract {} : no attributes", s);
return new AttributeSet();
return fromPath(s);
}


Expand All @@ -115,6 +132,12 @@ public AttributeSet extract(HasMetadata o) {
if (!Strings.isNullOrEmpty(o.getMetadata().getNamespace())) {
attributes = attributes.add(new Attribute(NAMESPACE, o.getMetadata().getNamespace()));
}

if (o.getMetadata().getLabels() != null) {
for (Map.Entry<String, String> label : o.getMetadata().getLabels().entrySet()) {
attributes = attributes.add(new Attribute(LABEL_KEY_PREFIX + label.getKey(), label.getValue()));
}
}
return attributes;
}

Expand Down Expand Up @@ -165,6 +188,22 @@ else if (kind.endsWith("s")) {
return attributes;
}

private static AttributeSet extractQueryParameters(HttpUrl url) {
AttributeSet attributes = new AttributeSet();
String labelSelector = url.queryParameter("labelSelector");
if (labelSelector != null) {
for (String requirement : labelSelector.split(",")) {
Matcher m = LABEL_REQUIREMENT_EQUALITY.matcher(requirement);
if (m.matches()) {
attributes = attributes.add(new Attribute(LABEL_KEY_PREFIX + m.group(KEY), m.group(VALUE)));
} else {
LOGGER.warn("Ignoring unsupported label requirement: {}", requirement);
}
}
}
return attributes;
}

private static HasMetadata toKubernetesResource(String s) {
try (InputStream stream = new ByteArrayInputStream(s.getBytes(StandardCharsets.UTF_8.name()))) {
return Serialization.unmarshal(stream);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import io.fabric8.kubernetes.api.model.PodBuilder;
import io.fabric8.mockwebserver.crud.Attribute;
import io.fabric8.mockwebserver.crud.AttributeSet;
import java.util.HashMap;
import java.util.Map;
import org.junit.Assert;
import org.junit.Test;

Expand Down Expand Up @@ -84,6 +86,20 @@ public void shouldHandleResource() {

}

@Test
public void shouldHandleResourceWithLabel() {
KubernetesAttributesExtractor extractor = new KubernetesAttributesExtractor();
Map<String, String> labels = new HashMap<>();
labels.put("name", "myname");
Pod pod = new PodBuilder().withNewMetadata().withLabels(labels).endMetadata().build();

AttributeSet attributes = extractor.extract(pod);

AttributeSet expected = new AttributeSet();
expected = expected.add(new Attribute("labels:name", "myname"));
Assert.assertTrue("Expected " + attributes + " to match " + expected, attributes.matches(expected));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a comment:
The project already imports AssertJ, why use JUnit assertions which are not readable at all?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JUnit assertions are used through the rest of the file. I'm new to the project so I couldn't say why - I just aim for consistency.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know I know, no worries.

}

@Test
public void shouldHandleKindWithoutVersion() {
KubernetesAttributesExtractor extractor = new KubernetesAttributesExtractor();
Expand Down Expand Up @@ -151,4 +167,45 @@ public void shouldHandleCrds() {
Assert.assertTrue("Expected " + attributes + " to match " + expected, attributes.matches(expected));
}

@Test
public void shouldHandleLabelSelectorsWithOneLabel() {
KubernetesAttributesExtractor extractor = new KubernetesAttributesExtractor();
AttributeSet attributes = extractor.extract("/api/v1/namespaces/myns/pods/mypod?labelSelector=name%3Dmyname");

AttributeSet expected = new AttributeSet();
expected = expected.add(new Attribute("labels:name", "myname"));
Assert.assertTrue("Expected " + attributes + " to match " + expected, attributes.matches(expected));
}

@Test
public void shouldHandleLabelSelectorsWithDoubleEquals() {
KubernetesAttributesExtractor extractor = new KubernetesAttributesExtractor();
AttributeSet attributes = extractor.extract("/api/v1/namespaces/myns/pods/mypod?labelSelector=name%3D%3Dmyname");

AttributeSet expected = new AttributeSet();
expected = expected.add(new Attribute("labels:name", "myname"));
Assert.assertTrue("Expected " + attributes + " to match " + expected, attributes.matches(expected));
}

@Test
public void shouldHandleLabelSelectorsWithTwoLabels() {
KubernetesAttributesExtractor extractor = new KubernetesAttributesExtractor();
AttributeSet attributes = extractor.extract("/api/v1/namespaces/myns/pods/mypod?labelSelector=name%3Dmyname,age%3D37");

AttributeSet expected = new AttributeSet();
expected = expected.add(new Attribute("labels:name", "myname"));
expected = expected.add(new Attribute("labels:age", "37"));
Assert.assertTrue("Expected " + attributes + " to match " + expected, attributes.matches(expected));
}

@Test
public void shouldHandleLabelSelectorsWithADomain() {
KubernetesAttributesExtractor extractor = new KubernetesAttributesExtractor();
AttributeSet attributes = extractor.extract("/api/v1/namespaces/myns/pods/mypod?labelSelector=example.com/name%3Dmyname");

AttributeSet expected = new AttributeSet();
expected = expected.add(new Attribute("labels:example.com/name", "myname"));
Assert.assertTrue("Expected " + attributes + " to match " + expected, attributes.matches(expected));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ public void testCrud() {
assertNotNull(aConfigMapList);
assertEquals(3, aConfigMapList.getItems().size());

aConfigMapList = client.configMaps().inAnyNamespace().withLabels(Collections.singletonMap("foo", "bar")).list();
assertNotNull(aConfigMapList);
assertEquals(2, aConfigMapList.getItems().size());

aConfigMapList = client.configMaps().inNamespace("ns1").list();
assertNotNull(aConfigMapList);
assertEquals(2, aConfigMapList.getItems().size());
Expand All @@ -76,10 +80,6 @@ public void testCrud() {
assertNotNull(aConfigMapList);
assertEquals(1, aConfigMapList.getItems().size());

aConfigMapList = client.configMaps().inAnyNamespace().withLabels(Collections.singletonMap("foo", "bar")).list();
assertNotNull(aConfigMapList);
assertEquals(2, aConfigMapList.getItems().size());

configmap2 = client.configMaps().inNamespace("ns1").withName("configmap2").edit().addToData("II", "TWO").done();
assertNotNull(configmap2);
assertEquals("TWO", configmap2.getData().get("II"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ public void testCrud() {
assertNotNull(podList);
assertEquals(0, podList.getItems().size());

// test listing with labels
podList = client.pods().inAnyNamespace().withLabels(Collections.singletonMap("testKey", "testValue")).list();
assertNotNull(podList);
assertEquals(2, podList.getItems().size());

podList = client.pods().inNamespace("ns1").list();
assertNotNull(podList);
assertEquals(2, podList.getItems().size());
Expand All @@ -68,11 +73,6 @@ public void testCrud() {
assertNotNull(podList);
assertEquals(2, podList.getItems().size());

// test listing with labels
podList = client.pods().inAnyNamespace().withLabels(Collections.singletonMap("testKey", "testValue")).list();
assertNotNull(podList);
assertEquals(2, podList.getItems().size());

// test update
pod2 = client.pods().inNamespace("ns1").withName("pod2").edit()
.editMetadata().addToLabels("key1", "value1").endMetadata().done();
Expand Down