Skip to content

Commit

Permalink
fix: better encapsulation of KubernetesEnvBuildItem behavior
Browse files Browse the repository at this point in the history
This is needed to be able to address quarkusio#9629. Things are still not
completely working but getting there.
  • Loading branch information
metacosm committed Jun 3, 2020
1 parent 3846503 commit df1a347
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 169 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
import io.quarkus.builder.item.MultiBuildItem;

public final class KubernetesEnvBuildItem extends MultiBuildItem {

public enum EnvType {
var(false),
field(false),
secret(true),
configmap(true);
configmap(true),
keyFromConfigmap(false),
keyFromSecret(false);

public final boolean allowMultipleDefinitions;

Expand Down Expand Up @@ -39,69 +40,91 @@ public boolean mightConflictWith(EnvType type) {

private final String name;
private final String value;
private final String configmap;
private final String secret;
private final String field;
private final EnvType type;
private final String target;
private final boolean oldStyle;

public static EnvType getEnvType(String secret, String configmap, String field) {
final EnvType type;
if (secret != null) {
type = EnvType.secret;
} else if (configmap != null) {
type = EnvType.configmap;
} else if (field != null) {
type = EnvType.field;
} else {
type = EnvType.var;
}
return type;
public static KubernetesEnvBuildItem createFromField(String name, String targetField, String target,
boolean... oldStyle) {
return create(name, null, null, null, targetField, target, oldStyle.length >= 1 && oldStyle[0]);
}

public static KubernetesEnvBuildItem createFromConfigMap(String configMapName, String target, boolean... oldStyle) {
return create(configMapName, null, null, configMapName, null, target, oldStyle.length >= 1 && oldStyle[0]);
}

public static KubernetesEnvBuildItem createSimpleVar(String name, String value, String target) {
return new KubernetesEnvBuildItem(EnvType.var, name, value, target, false);
public static KubernetesEnvBuildItem createFromSecret(String secretName, String target, boolean... oldStyle) {
return create(secretName, null, secretName, null, null, target, oldStyle.length >= 1 && oldStyle[0]);
}

public static KubernetesEnvBuildItem create(EnvType type, String name, String value, String target) {
return create(type, name, value, target, false);
public static KubernetesEnvBuildItem createSimpleVar(String name, String value, String target,
boolean... oldStyle) {
return create(name, value, null, null, null, target, oldStyle.length >= 1 && oldStyle[0]);
}

public static KubernetesEnvBuildItem create(EnvType type, String name, String value, String target,
boolean oldStyle) {
return new KubernetesEnvBuildItem(type, name, value, target, oldStyle);
public static KubernetesEnvBuildItem create(String name, String value, String secret, String configmap, String field,
String target, boolean... oldStyle) throws IllegalArgumentException {
final boolean secretPresent = secret != null;
final boolean configmapPresent = configmap != null;
final boolean valuePresent = value != null;
final boolean fieldPresent = field != null;
if (valuePresent) {
if (secretPresent && configmapPresent) {
throw new IllegalArgumentException(String.format(
"'%s' env var can't simultaneously take its value from '%s' configmap & '%s' secret",
name, value, configmap, secret));
}
if (fieldPresent) {
throw new IllegalArgumentException(String.format(
"'%s' env var can't simultaneously have a '%s' value & take is value from the '%s' field",
name, value, field));
}
}
final EnvType type;
if (secretPresent) {
type = valuePresent ? EnvType.keyFromSecret : EnvType.secret;
} else if (configmapPresent) {
type = valuePresent ? EnvType.keyFromConfigmap : EnvType.configmap;
} else if (field != null) {
type = EnvType.field;
} else {
type = EnvType.var;
}
return new KubernetesEnvBuildItem(name, value, configmap, secret, field, type, target,
oldStyle.length >= 1 && oldStyle[0]);
}

KubernetesEnvBuildItem(EnvType type, String name, String value, String target, boolean oldStyle) {
KubernetesEnvBuildItem(String name, String value, String configmap, String secret, String field, EnvType type,
String target, boolean oldStyle) {
this.name = name;
this.value = value;
this.configmap = configmap;
this.secret = secret;
this.field = field;
this.type = type;
this.target = target;
this.oldStyle = oldStyle;
}

public String getConfigMap() {
return getValueIfMatching(EnvType.configmap);
return configmap;
}

public String getSecret() {
return getValueIfMatching(EnvType.secret);
return secret;
}

public String getField() {
return getValueIfMatching(EnvType.field);
}

public String getVar() {
return getValueIfMatching(EnvType.var);
return field;
}

public boolean isOldStyle() {
return oldStyle;
}

private String getValueIfMatching(EnvType type) {
return this.type == type ? value : null;
}

public String getName() {
return name;
}
Expand Down Expand Up @@ -129,24 +152,29 @@ public boolean equals(Object o) {

if (!name.equals(that.name))
return false;
if (!value.equals(that.value))
if (value != null ? !value.equals(that.value) : that.value != null)
return false;
if (type != that.type)
if (configmap != null ? !configmap.equals(that.configmap) : that.configmap != null)
return false;
return target.equals(that.target);
if (secret != null ? !secret.equals(that.secret) : that.secret != null)
return false;
if (field != null ? !field.equals(that.field) : that.field != null)
return false;
return type == that.type;
}

@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + value.hashCode();
result = 31 * result + (value != null ? value.hashCode() : 0);
result = 31 * result + (configmap != null ? configmap.hashCode() : 0);
result = 31 * result + (secret != null ? secret.hashCode() : 0);
result = 31 * result + (field != null ? field.hashCode() : 0);
result = 31 * result + type.hashCode();
result = 31 * result + target.hashCode();
return result;
}

public String toString() {
return String.format("'%s' env var with value '%s'", this.getType().name(), this.getValue());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* Copyright 2020 Red Hat, Inc. and/or its affiliates.
*
* <p>
* Licensed 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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 io.quarkus.kubernetes.spi;

import static io.quarkus.kubernetes.spi.KubernetesEnvBuildItem.create;
import static io.quarkus.kubernetes.spi.KubernetesEnvBuildItem.EnvType.configmap;
import static io.quarkus.kubernetes.spi.KubernetesEnvBuildItem.EnvType.var;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;

import org.junit.jupiter.api.Test;

/**
* @author <a href="[email protected]">Christophe Laprun</a>
*/
public class KubernetesEnvBuildItemTest {

private static final String TARGET = "target";
private static final String VALUE = "value";
private static final String NAME = "name";

@Test
public void testCreateSimpleVarFromEnvConfig() {
final KubernetesEnvBuildItem item = create(NAME, VALUE, null, null, null, TARGET);
assertEquals(var, item.getType());
assertEquals(NAME, item.getName());
assertEquals(VALUE, item.getValue());
assertEquals(TARGET, item.getTarget());
assertNull(item.getConfigMap());
assertNull(item.getSecret());
assertNull(item.getField());
}

@Test
public void testCreateLoadFromConfigMapFromEnvConfig() {
final KubernetesEnvBuildItem item = create(NAME, null, null, VALUE, null, TARGET);
assertEquals(configmap, item.getType());
assertEquals(NAME, item.getName());
assertNull(item.getValue());
assertEquals(VALUE, item.getConfigMap());
assertNull(item.getSecret());
assertNull(item.getField());
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package io.quarkus.kubernetes.deployment;

import static io.quarkus.kubernetes.spi.KubernetesEnvBuildItem.EnvType.*;

import java.util.Collection;
import java.util.Map;

Expand Down Expand Up @@ -44,21 +42,23 @@ default Collection<KubernetesEnvBuildItem> convertToBuildItems() {
// first process old-style configuration, this relies on each configuration having a name
final String target = getTargetPlatformName();
getEnvVars().forEach((key, envConfig) -> {
envConfig.secret.ifPresent(s -> validator.process(KubernetesEnvBuildItem.create(secret, s, s, target, true)));
envConfig.configmap
.ifPresent(cm -> validator.process(KubernetesEnvBuildItem.create(configmap, cm, cm, target, true)));
envConfig.field.ifPresent(f -> validator.process(KubernetesEnvBuildItem.create(field, key, f, target, true)));
envConfig.value.ifPresent(v -> validator.process(KubernetesEnvBuildItem.create(var, key, v, target, true)));
validator.process(key, envConfig.value, envConfig.secret, envConfig.configmap, envConfig.field, target);
/*
* envConfig.secret.ifPresent(s -> validator.process(KubernetesEnvBuildItem.create(secret, s, s, target, true)));
* envConfig.configmap
* .ifPresent(cm -> validator.process(KubernetesEnvBuildItem.create(configmap, cm, cm, target, true)));
* envConfig.field.ifPresent(f -> validator.process(KubernetesEnvBuildItem.create(field, key, f, target, true)));
* envConfig.value.ifPresent(v -> validator.process(KubernetesEnvBuildItem.create(var, key, v, target, true)));
*/
});

// override old-style with newer versions if present
final EnvVarsConfig c = getEnv();
c.vars.forEach((k, v) -> validator.process(KubernetesEnvBuildItem.create(var, k, v, target)));
c.fields.forEach((k, v) -> validator.process(KubernetesEnvBuildItem.create(field, k, v, target)));
c.configmaps.ifPresent(
cl -> cl.forEach(cm -> validator.process(KubernetesEnvBuildItem.create(configmap, cm, cm, target))));
c.secrets.ifPresent(
sl -> sl.forEach(s -> validator.process(KubernetesEnvBuildItem.create(secret, s, s, target))));
c.vars.forEach((k, v) -> validator.process(KubernetesEnvBuildItem.createSimpleVar(k, v, target)));
c.fields.forEach((k, v) -> validator.process(KubernetesEnvBuildItem.createFromField(k, v, target)));
c.configmaps
.ifPresent(cl -> cl.forEach(cm -> validator.process(KubernetesEnvBuildItem.createFromConfigMap(cm, target))));
c.secrets.ifPresent(sl -> sl.forEach(s -> validator.process(KubernetesEnvBuildItem.createFromSecret(s, target))));

return validator.getBuildItems();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ public class EnvVarValidator {
private final Map<ItemKey, KubernetesEnvBuildItem> items = new HashMap<>();
private final Set<String> knownNames = new HashSet<>();
private final Map<String, Set<KubernetesEnvBuildItem>> conflicting = new HashMap<>();
private final Set<String> errors = new HashSet<>();

void process(String name, Optional<String> value, Optional<String> secret, Optional<String> configmap,
Optional<String> field, String target) {
try {
final KubernetesEnvBuildItem kebi = KubernetesEnvBuildItem.create(name, value.orElse(null),
secret.orElse(null), configmap.orElse(null), field.orElse(null), target, true);
process(kebi);
} catch (IllegalArgumentException e) {
errors.add(e.getMessage());
}
}

/**
* Processes the specified {@link KubernetesEnvBuildItem} to check whether it's valid with respect to the set of already
Expand Down Expand Up @@ -84,22 +96,29 @@ private void addError(KubernetesEnvBuildItem item, KubernetesEnvBuildItem existi
* @throws IllegalArgumentException if the processed items result in an invalid configuration
*/
Collection<KubernetesEnvBuildItem> getBuildItems() {
if (conflicting.isEmpty()) {
if (conflicting.isEmpty() && errors.isEmpty()) {
return items.values();
}
throw new IllegalArgumentException(getError());
}

private String getError() {
String error = "Found conflicts in environment variable definitions:\n";
error += conflicting.entrySet().stream()
.map(e -> {
final String conflicting = e.getValue().stream()
.map(Object::toString)
.collect(Collectors.joining(" redefined as "));
return String.format("\t\t- '%s': first defined as %s", e.getKey(), conflicting);
})
.collect(Collectors.joining("\n"));
String error = "Errors:\n";
if (!conflicting.isEmpty()) {
error += "Found conflicts in environment variable definitions:\n";
error += conflicting.entrySet().stream()
.map(e -> {
final String conflicting = e.getValue().stream()
.map(Object::toString)
.collect(Collectors.joining(" redefined as "));
return String.format("\t\t- '%s': first defined as %s", e.getKey(), conflicting);
})
.collect(Collectors.joining("\n"));
}
if (!errors.isEmpty()) {
error += "Invalid declarations:\n";
error += errors.stream().map(s -> "\t\t- " + s).collect(Collectors.joining("\n"));
}
return error;
}

Expand Down
Loading

0 comments on commit df1a347

Please sign in to comment.