Skip to content

Commit

Permalink
Merge branch 'master' of github.com:fabric8io/kubernetes-client into
Browse files Browse the repository at this point in the history
4201
  • Loading branch information
shawkins committed Nov 17, 2022
2 parents 64134d0 + 4ab4dbb commit c741a07
Show file tree
Hide file tree
Showing 58 changed files with 1,958 additions and 417 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,25 @@
### 6.3-SNAPSHOT

#### Bugs
* Fix #4159: ensure the token refresh obeys how the Config was created
* Fix #4491: added a more explicit shutdown exception for okhttp
* Fix #4510: Fix StackOverflow on cyclic references involving collections.
* Fix #4534: Java Generator CLI default handling of skipGeneratedAnnotations
* Fix #4535: The shell command string will now have single quotes sanitized
* Fix #4543: (Java Generator) additionalProperties JsonAny setter method generated as setAdditionalProperty
* Fix #4547: preventing timing issues with leader election cancel
* Fix #4569: fixing jdk httpclient regression with 0 timeouts

#### Improvements
* Fix #4355: for exec, attach, upload, and copy operations the container id/name will be validated or chosen prior to the remote call. You may also use the kubectl.kubernetes.io/default-container annotation to specify the default container.
* Fix #4530: generalizing the Serialization logic to allow for primitive values and clarifying the type expectations.
* Fix #4201: Removed sendAsync from the individual http client implementations

#### Dependency Upgrade

#### New Features
* Fix #4136: added support for fieldValidation as a dsl method for POST/PUT/PATCH operations
* Fix #3896: added dsl support for server side apply

#### _**Note**_: Breaking changes in the API

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class ClassDependenciesVisitor extends TypedVisitor<TypeDefBuilder> {
private static final Map<String, Set<String>> traversedClasses = new HashMap<>();
private static final Map<String, Set<String>> crdNameToCrClass = new HashMap<>();
private final Set<String> classesForCR;
private final Set<String> processed = new HashSet<>();
private final Set<ClassRef> processed = new HashSet<>();

public ClassDependenciesVisitor(String crClassName, String crdName) {
// need to record all classes associated with the different versions of the CR (not the CRD spec)
Expand All @@ -49,7 +49,7 @@ public void visit(TypeDefBuilder builder) {
// note that we cannot simply check the traversed class set to know if a class has been processed because classes
// are usually added to the traversed set before they're looked at in depth
final String className = type.getFullyQualifiedName();
if (ignore(className) || processed.contains(className)) {
if (ignore(className)) {
return;
}

Expand All @@ -74,19 +74,21 @@ public void visit(TypeDefBuilder builder) {

// add classes from extends list
type.getExtendsList().forEach(this::processTypeRef);

// add class to the processed classes
processed.add(className);
}

private boolean ignore(String className) {
return (className.startsWith("java.") && !className.startsWith("java.util.")) || className.startsWith("com.fasterxml.jackson") || className.startsWith("jdk.");
return (className.startsWith("java.") && !className.startsWith("java.util."))
|| className.startsWith("com.fasterxml.jackson") || className.startsWith("jdk.");
}

private void processTypeRef(TypeRef t) {
if (t instanceof ClassRef) {
ClassRef classRef = (ClassRef) t;
visit(new TypeDefBuilder(Types.typeDefFrom(classRef)));
// only process the class reference if we haven't already
// note that the references are stored in the set including type arguments, so List<A> and List<B> are not the same
if (processed.add(classRef)) {
visit(new TypeDefBuilder(Types.typeDefFrom(classRef)));
}
}
}

Expand All @@ -101,7 +103,7 @@ public static Set<String> getDependentClasses(String crClassName) {
public static Set<String> getDependentClassesFromCRDName(String crdName) {
// retrieve all dependent classes that might affect any of the CR versions
return crdNameToCrClass.get(crdName).stream()
.flatMap(crClassName -> traversedClasses.get(crClassName).stream())
.collect(Collectors.toSet());
.flatMap(crClassName -> traversedClasses.get(crClassName).stream())
.collect(Collectors.toSet());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Copyright (C) 2015 Red Hat, Inc.
*
* 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
*
* 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 io.fabric8.crd.example.cyclic;

import io.fabric8.kubernetes.api.model.Namespaced;
import io.fabric8.kubernetes.client.CustomResource;
import io.fabric8.kubernetes.model.annotation.Group;
import io.fabric8.kubernetes.model.annotation.Version;

@Group("sample.fabric8.io")
@Version("v1alpha1")
public class CyclicList extends CustomResource<CyclicListSpec, CyclicStatus> implements Namespaced {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Copyright (C) 2015 Red Hat, Inc.
*
* 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
*
* 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 io.fabric8.crd.example.cyclic;

import java.util.List;

public class CyclicListSpec {
private List<RefList> ref;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Copyright (C) 2015 Red Hat, Inc.
*
* 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
*
* 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 io.fabric8.crd.example.cyclic;

import java.util.List;

public class RefList {

private List<RefList> ref;

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import io.fabric8.crd.example.basic.BasicSpec;
import io.fabric8.crd.example.basic.BasicStatus;
import io.fabric8.crd.example.cyclic.Cyclic;
import io.fabric8.crd.example.cyclic.CyclicList;
import io.fabric8.crd.example.inherited.*;
import io.fabric8.crd.example.joke.Joke;
import io.fabric8.crd.example.joke.JokeRequest;
Expand Down Expand Up @@ -216,6 +217,19 @@ void generatingACycleShouldFail() {
"An IllegalArgument Exception hasn't been thrown when generating a CRD with cyclic references");
}

@Test
void generatingACycleInListShouldFail() {
final CRDGenerator generator = new CRDGenerator()
.customResourceClasses(CyclicList.class)
.forCRDVersions("v1", "v1beta1")
.withOutput(output);

assertThrows(
IllegalArgumentException.class,
() -> generator.detailedGenerate(),
"An IllegalArgument Exception hasn't been thrown when generating a CRD with cyclic references");
}

@Test
void notGeneratingACycleShouldSucceed() {
final CRDGenerator generator = new CRDGenerator()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public HttpClient build() {
return new JdkHttpClientImpl(this, client.getHttpClient(), this.requestConfig);
}
java.net.http.HttpClient.Builder builder = clientFactory.createNewHttpClientBuilder();
if (connectTimeout != null) {
if (connectTimeout != null && !java.time.Duration.ZERO.equals(connectTimeout)) {
builder.connectTimeout(connectTimeout);
}
if (sslContext != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.net.http.HttpResponse.ResponseInfo;
import java.net.http.WebSocketHandshakeException;
import java.nio.ByteBuffer;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -389,8 +390,9 @@ public CompletableFuture<WebSocketResponse> internalBuildAsync(JdkWebSocketImpl.
}
// the Watch logic sets a websocketTimeout as the readTimeout
// TODO: this should probably be made clearer in the docs
if (this.builder.getReadTimeout() != null) {
newBuilder.connectTimeout(this.builder.getReadTimeout());
Duration readTimeout = this.builder.getReadTimeout();
if (readTimeout != null && !java.time.Duration.ZERO.equals(readTimeout)) {
newBuilder.connectTimeout(readTimeout);
}

AtomicLong queueSize = new AtomicLong();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public Builder setHeader(String k, String v) {
}

public Builder timeout(Duration duration) {
if (duration != null) {
if (duration != null && !Duration.ZERO.equals(duration)) {
builder.timeout(duration);
}
return this;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Copyright (C) 2015 Red Hat, Inc.
*
* 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
*
* 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 io.fabric8.kubernetes.client.jdkhttp;

import io.fabric8.kubernetes.client.http.HttpClient;
import org.junit.jupiter.api.Test;

import java.util.concurrent.TimeUnit;

import static org.junit.jupiter.api.Assertions.assertNotNull;

class JdkHttpClientBuilderTest {

@Test
void testZeroTimeouts() {
JdkHttpClientFactory factory = new JdkHttpClientFactory();
JdkHttpClientBuilderImpl builder = factory.newBuilder();

// should build and be usable without an issue
try (HttpClient client = builder.readTimeout(0, TimeUnit.MILLISECONDS).connectTimeout(0, TimeUnit.MILLISECONDS)
.writeTimeout(0,
TimeUnit.MILLISECONDS)
.build();) {
assertNotNull(client.newHttpRequestBuilder().uri("http://localhost").build());
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.EnumDeclaration;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.expr.*;
import com.github.javaparser.ast.stmt.BlockStmt;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.utils.StringEscapeUtils;
import io.fabric8.java.generator.Config;
Expand All @@ -37,15 +39,6 @@

public class JObject extends AbstractJSONSchema2Pojo implements JObjectExtraAnnotations {

private static final Set<String> IGNORED_FIELDS = new HashSet<>();

static {
IGNORED_FIELDS.add("description");
IGNORED_FIELDS.add("schema");
IGNORED_FIELDS.add("example");
IGNORED_FIELDS.add("examples");
}

private final String type;
private final String className;
private final String pkg;
Expand Down Expand Up @@ -97,19 +90,17 @@ public JObject(
}

for (Map.Entry<String, JSONSchemaProps> field : fields.entrySet()) {
if (!IGNORED_FIELDS.contains(field.getKey())) {
String nextPrefix = (config.getPrefixStrategy() == Config.Prefix.ALWAYS) ? classPrefix : "";
String nextSuffix = (config.getSuffixStrategy() == Config.Suffix.ALWAYS) ? classSuffix : "";
this.fields.put(
field.getKey(),
AbstractJSONSchema2Pojo.fromJsonSchema(
field.getKey(),
field.getValue(),
nextPackagePath,
nextPrefix,
nextSuffix,
config));
}
String nextPrefix = (config.getPrefixStrategy() == Config.Prefix.ALWAYS) ? classPrefix : "";
String nextSuffix = (config.getSuffixStrategy() == Config.Suffix.ALWAYS) ? classSuffix : "";
this.fields.put(
field.getKey(),
AbstractJSONSchema2Pojo.fromJsonSchema(
field.getKey(),
field.getValue(),
nextPackagePath,
nextPrefix,
nextSuffix,
config));
}
}
}
Expand Down Expand Up @@ -253,7 +244,19 @@ public GeneratorResult generateJava() {
}

if (prop.getDefaultValue() != null) {
objField.getVariable(0).setInitializer(generateDefaultInitializerExpression(prop));
Expression primitiveDefault = generatePrimitiveDefaultInitializerExpression(prop);

if (primitiveDefault != null) {
objField.getVariable(0).setInitializer(primitiveDefault);
} else {
objField.getVariable(0).setInitializer(
new NameExpr(
"io.fabric8.kubernetes.client.utils.Serialization.unmarshal("
+ "\"" + StringEscapeUtils.escapeJava(Serialization.asJson(prop.getDefaultValue())) + "\""
+ ", "
+ prop.getClassType() + ".class"
+ ")"));
}
}
} catch (Exception cause) {
throw new JavaGeneratorException(
Expand All @@ -280,6 +283,13 @@ public GeneratorResult generateJava() {

objField.createGetter().addAnnotation("com.fasterxml.jackson.annotation.JsonAnyGetter");
objField.createSetter().addAnnotation("com.fasterxml.jackson.annotation.JsonAnySetter");

MethodDeclaration additionalSetter = clz.addMethod("setAdditionalProperty", Modifier.Keyword.PUBLIC);
additionalSetter.addAnnotation("com.fasterxml.jackson.annotation.JsonAnySetter");
additionalSetter.addParameter("String", "key");
additionalSetter.addParameter("Object", "value");
additionalSetter
.setBody(new BlockStmt().addStatement(new NameExpr("this." + Keywords.ADDITIONAL_PROPERTIES + ".put(key, value);")));
}

buffer.add(new GeneratorResult.ClassResult(this.className, cu));
Expand All @@ -288,17 +298,25 @@ public GeneratorResult generateJava() {
}

/**
* This method is responsible for creating an expression that will initialize the default value.
* This method is responsible for creating an expression that will initialize the default value if primitive
*
* @return a {@link Expression} instance that contains a call to the
* {@link Serialization#unmarshal(String, Class)} method.
*/
private Expression generateDefaultInitializerExpression(AbstractJSONSchema2Pojo prop) {
return new NameExpr(
"io.fabric8.kubernetes.client.utils.Serialization.unmarshal("
+ "\"" + StringEscapeUtils.escapeJava(Serialization.asJson(prop.getDefaultValue())) + "\""
+ ", "
+ prop.getClassType() + ".class"
+ ")");
private Expression generatePrimitiveDefaultInitializerExpression(AbstractJSONSchema2Pojo prop) {
if (prop.getDefaultValue().isValueNode()) {
String value = prop.getDefaultValue().toString();
if (prop.getClassType().equals("Long") && prop.getDefaultValue().canConvertToLong()) {
return new LongLiteralExpr(value + "L");
} else if (prop.getClassType().equals("Float") && prop.getDefaultValue().isFloatingPointNumber()) {
return new DoubleLiteralExpr(value + "f");
} else if (prop.getClassType().equals("Boolean") && prop.getDefaultValue().isBoolean()) {
return new BooleanLiteralExpr(prop.getDefaultValue().booleanValue());
} else {
return new NameExpr(value);
}
} else {
return null;
}
}
}

0 comments on commit c741a07

Please sign in to comment.